Epoll和eventfd之“惊群(thundering herd)”问题

September 18, 2013 | 1 Minute Read

微博上面有人回复我说epoll的的惊群问题,不是很理解。带着疑问看一下linux kernel的epoll 和 eventfd相关的代码。因为我局的epoll和eventfd一起使用应该是比较简单的。

?

http://lxr.linux.no/linux+v3.11/fs/eventpoll.c

http://lxr.linux.no/linux+v3.11/fs/eventfd.c



------------------

情况1 调用 epoll_wait

epoll_wait

   ep_poll

      __add_wait_queue_exclusive(&ep->wq, &wait);    // 插入队列时有WQ_FLAG_EXCLUSIVE标志,唤醒是只唤醒一个不会有“惊群(Thundering herd)”问题

      freezable_schedule_hrtimeout_range(to, slack,  // 休眠,等待ep_poll_callback函数里面唤醒这个

      __remove_wait_queue(&ep->wq, &wait);   //从休眠中返回就把自己从wait queue队列里面删除了

      ep_send_events            //提交事件到用户空间

          ep_scan_ready_list

       	    ep_read_events_proc

                ep_item_poll

                    file->f_op->poll

                        eventfd_poll

                           poll_wait(file, &ctx->wqh, wait)  //调用_qproc 回调

                              ep_ptable_queue_proc   

      



----------------------------

情况2 调用普通的 poll

ep_eventpoll_poll

    poll_wait   // p->_qproc

    ep_poll_readyevents_proc

      ep_scan_ready_list

          下面同情况1





         



--------------------------

ep_poll_callback   // 由eventfd 里面满足条件时调用这个

   list_add_tail(&epi->rdllink, &ep->rdllist);  //记录触发的事件

   wake_up_locked(&ep->wq); 

         __wake_up_locked((x), TASK_NORMAL, 1)    //1表示只唤醒wait queue里面一个带有WQ_FLAG_EXCLUSIVEwaitqueue

               __wake_up_common(q, mode, nr, 0, NULL);

                //唤醒在epoll_wait ep_poll 里面的等待线程,因为前面是调用了__add_wait_queue_exclusive他、添加进去的



------------

epoll_ctl  函数初始化时把ep_poll_callback回调函数注册到要监控的file的wait queue的里面去。



epoll_ctl

  ep_remove      

     ep_unregister_pollwait 

         ep_remove_wait_queue(pwq); //删掉 ep_insert时注册到file的wait queue

  ep_insert

    ep_set_ffd(&epi->ffd, tfile, fd);

    init_poll_funcptr(&epq.pt, ep_ptable_queue_proc); //注册_qproc 回调

    ep_item_poll(epi, &epq.pt);

          file->f_op->poll

              eventfd_poll

                    poll_wait(file, &ctx->wqh, wait)  //调用_qproc 回调

                        ep_ptable_queue_proc   

                             add_wait_queue(whead, &pwq->wait)



/*

 * This is the callback that is used to add our wait queue to the

 * target file wakeup lists.

 */

ep_ptable_queue_proc

        init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);  //把ep_poll_callback注册为 wait_queue_t q->func 函数,同时初始化 q->flags = 0; 是没有WQ_FLAG_EXCLUSIVE标志的

        add_wait_queue(whead, &pwq->wait);    ///这里的不像上面的__add_wait_queue_exclusive一样设置WQ_FLAG_EXCLUSIVE标志的。

-------------------

eventfd里面满足条件时触发epoll的poll回调函数,唤醒epoll wait



eventfd_write

   wake_up_locked_poll(&ctx->wqh, POLLIN);

       __wake_up_locked_key((x), TASK_NORMAL, (void *) (m))

           __wake_up_common(q, mode, 1, 0, key);  //这里唤醒的时候应该有“惊群”,

                curr->func

                    ep_poll_callback



----------------------





所以根据上面代码知道

1. 如果多个进程或者线程里面 epoll_create创建的同一个“epoll file descriptor”做epoll_wait,那么是没有“惊群(Thundering herd)”问题的。

2. 如果在多个进行或者线程里面自己用 epoll_create创建的各自的“epoll file descriptor”,然后把同一个 eventfd的file descriptor通过epoll_ctl添加到各自的“epoll file descriptor”, 这个多个 “epoll file descriptor”同一个eventfd。

那么但eventfd有事件到来时, 这多个通过epoll wait休眠的进程或者线程是会被同时唤醒的。这就是所谓的“惊群(Thundering herd)”问题。



以后有时间测试一下看看是不是这样。 然后可以看一下socket的file desciptor 是不是行为也是像 eventfd一样的。