zoukankan      html  css  js  c++  java
  • epoll的ET和LT模式比较

    eventpoll是一种文件,它实现了一种机制利用一条rdllist队列来避免阻塞地进行poll。eventpoll归根到底还是在使用poll。而ET比LT高效,并不在于是否使用了poll,更不能说是因为LT使用了poll。通过阅读源代码就可以清楚看到对 ET 和 LT 处理的区别仅有一处,其余都相同。其实两者都在使用poll,只不过 ET 可避免多次在epoll_wait对不确定的rdllist进行重复poll检测。

    首先来看sys_poll,f_op->poll 和 eventpoll 文件的关系。

    sys_poll 可以通过wait机制,由被poll的文件的具体文件系统在文件发生状态变化时,通过wait设定的回调函数唤醒阻塞在poll的任务。
    而eventpoll 则是不阻塞地使用wait机制,它使用ep_poll_callback作为wait的回调函数,让被poll的文件的具体文件系统在文件发生状态变化时,通过这个ep_poll_callback将自身关联epi链入到 eventpoll文件的 rdllist 中去。
    对一个文件sys_poll时,会使用__poll_wait回调函数来阻塞等待事件唤醒。而将一个文件sys_epoll_ctl添加进eventpoll时,会使用ep_poll_callback实现异步的poll。
    这里要区分sys_poll和file_operations->poll,阻塞是因为sys_poll执行poll_schedule_timeout(),而file_operations->poll只是建立wait机制的使用(,但是我们不可能绕开sys_poll直接去使用具体文件系统的poll)。所以eventpoll才可以有别于sys_poll进行异步的poll(,不去阻塞等待),也就是说sys_poll使用阻塞的政策,epoll_ctl的EPOLL_CTL_ADD使用异步的政策。实际两者同样都在使用wait机制的回调。

    sys_epoll_wait只是关心eventpoll的rdllist队列是否为空,并且还必须对rdllist队列里面每一个关联的文件使用poll检测进行最终筛选。

    而et 和 lvl-tri模式的唯一不同的处理则只是在sys_epoll_wait过程中。
    调用路径为:
    sys_epoll_wait() > ep_send_evnets() > ep_scan_ready_list() > ep_send_events_proc()
    et 和 lvl-tri 模式的差异,仅仅是因为 ep_send_events_proc 一个小动作,影响了下一次的ep_scan_ready_list 处理。

    下面是ep_scan_ready_list的处理流程,从流程清楚可以找出差异来。

    0. ep_scan_ready_list 将rdllist 截出来收集到txlist
    1. 当ep_scan_ready_list进行ep_send_events_proc时,ep_poll_callback (使用wait机制对文件进行poll的异步回调) 将epi (一个文件关系到eventpoll的结构)链入到 ovflist
    2. 否则ep_poll_callback将epi 链入到 rdllist
    3. ep_send_events_proc 将对txlist的每个epi的进行poll检测状态
        如果满足状态
        a. 发送到用户空间,
        b. 并将 非EPOLLET的 epi 重新链入 rdllist
        * 差异就在这里,对于LT模式下次还得通过poll进行筛选,即使你已经将文件的读缓冲读完了。
    4. 在ep_scan_readly_list结束ep_send_events_proc后,会收集 ovflist 到 rdllist
    5. 将未能写到用户空间的 txlist合并回rdllist

    试想下面的情景:

    当从epoll_wait取得事件后,同样都将读事件的文件的缓冲读完,并且没有写入发生时,再次进入epoll_wait。
    在 et模式下,这个文件不会出现在eventpoll文件的rdllist中。
    而 lvl-tri模式下,尽管这个文件已经不可能是POLLIN状态了,但下一次epoll_wait时,必须进行一次poll检测状态后从而筛选掉。
    这个情景中,et模式比lvl-tri模式少了一次file_operations->poll检测,所以比较起来就有效得多了。
    当有N个文件,M次读事件条件下,lvl-tri就可能会浪费N*M次poll检测。
    但不论et模式还是lvl-tri模式,epoll_wait都必须对rdllist队列中每一个对应的文件使用poll检测进行筛选。

    static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
                       void *priv)
    {
        struct ep_send_events_data *esed = priv;
        int eventcnt;
        unsigned int revents;
        struct epitem *epi;
        struct epoll_event __user *uevent;
        struct wakeup_source *ws;
        poll_table pt;
    
        init_poll_funcptr(&pt, NULL);
    
        /*
         * We can loop without lock because we are passed a task private list.
         * Items cannot vanish during the loop because ep_scan_ready_list() is
         * holding "mtx" during this call.
         */
        for (eventcnt = 0, uevent = esed->events;
             !list_empty(head) && eventcnt < esed->maxevents;) {
            epi = list_first_entry(head, struct epitem, rdllink);
    
            /*
             * Activate ep->ws before deactivating epi->ws to prevent
             * triggering auto-suspend here (in case we reactive epi->ws
             * below).
             *
             * This could be rearranged to delay the deactivation of epi->ws
             * instead, but then epi->ws would temporarily be out of sync
             * with ep_is_linked().
             */
            ws = ep_wakeup_source(epi);
            if (ws) {
                if (ws->active)
                    __pm_stay_awake(ep->ws);
                __pm_relax(ws);
            }
    
            list_del_init(&epi->rdllink);
    
            revents = ep_item_poll(epi, &pt);    // 调用f_op->poll,但不是sys_poll
    
            /*
             * If the event mask intersect the caller-requested one,
             * deliver the event to userspace. Again, ep_scan_ready_list()
             * is holding "mtx", so no operations coming from userspace
             * can change the item.
             */
            if (revents) {
                if (__put_user(revents, &uevent->events) ||
                    __put_user(epi->event.data, &uevent->data)) {
                    list_add(&epi->rdllink, head);
                    ep_pm_stay_awake(epi);
                    return eventcnt ? eventcnt : -EFAULT;
                }
                eventcnt++;
                uevent++;
                if (epi->event.events & EPOLLONESHOT)
                    epi->event.events &= EP_PRIVATE_BITS;
                else if (!(epi->event.events & EPOLLET)) {
                    /*
                     * If this file has been added with Level
                     * Trigger mode, we need to insert back inside
                     * the ready list, so that the next call to
                     * epoll_wait() will check again the events
                     * availability. At this point, no one can insert
                     * into ep->rdllist besides us. The epoll_ctl()
                     * callers are locked out by
                     * ep_scan_ready_list() holding "mtx" and the
                     * poll callback will queue them in ep->ovflist.
                     */
                    list_add_tail(&epi->rdllink, &ep->rdllist);
                    ep_pm_stay_awake(epi);
                }
            }
        }
    
        return eventcnt;
    }


    eventpoll是一个文件,同样可以使用sys_poll对其进行阻塞poll检测。
    ep_scan_ready_list() 配合 ep_read_events_proc() 使用在eventpoll的file_operations->poll中。

    (ep_scan_ready_list() 配合 ep_send_events_proc() 使用在epoll_wait)

    用于eventpoll文件被poll
    1. 检查rdllist中是否包含至少一个满足期望轮询到的状态。
    2. 对rdllist中的每一个epi进行poll检测:
        a. 满足就返回;
        b. 不满足,顺便称出rdllist。

  • 相关阅读:
    BZOJ3270: 博物馆【概率DP】【高斯消元】
    SpringCloud入门概述
    微服务的技术栈
    Markdown基础教程
    分布式架构和垂直架构
    ZooKeeper和CAP理论及一致性原则
    zookeer集群的特性
    java操作zookeeper
    Zookeeper命令使用
    Zookeeper的安装
  • 原文地址:https://www.cnblogs.com/bbqzsl/p/7060819.html
Copyright © 2011-2022 走看看