epoll是如何监控多个描述符及如何获得通知(2)

简介: 作者:gfree.wind@gmail.com博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net  微博:weibo.com/glinuxer QQ技术群:4367710   本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
微博:weibo.com/glinuxer
QQ技术群:4367710
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。

========================================================================================================

上文书说到,epoll是如何加到每个监控描述符的wait queue中,这只是第一步。上次也提过,epoll实际上也是一个阻塞操作,只不过是可以同时监控多个文件描述符。下面看一下epoll_wait->ep_poll的实现。

epoll既然是阻塞的,必然需要wait queue。但是这个不能使用监控的文件描述符的wait queue,epoll自己本身也是一个虚拟的文件系统。epoll_create的返回值也是一个文件描述符。Unix下,一切皆是文件嘛。

所以epoll的实现代码如下:

  1.         init_waitqueue_entry(&wait, current);
  2.         __add_wait_queue_exclusive(&ep->wq, &wait);

  3.         for (;;) {
  4.             /*
  5.              * We don't want to sleep if the ep_poll_callback() sends us
  6.              * a wakeup in between. That's why we set the task state
  7.              * to TASK_INTERRUPTIBLE before doing the checks.
  8.              */
  9.             set_current_state(TASK_INTERRUPTIBLE);
  10.             if (ep_events_available(ep) || timed_out)
  11.                 break;
  12.             if (signal_pending(current)) {
  13.                 res = -EINTR;
  14.                 break;
  15.             }

  16.             spin_unlock_irqrestore(&ep->lock, flags);
  17.             if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
  18.                 timed_out = 1;

  19.             spin_lock_irqsave(&ep->lock, flags);
  20.         }
  21.         __remove_wait_queue(&ep->wq, &wait);
这里epoll_wait是将当前进程添加到epoll自身的wait queue中。那么问题来了,前文说到epoll已经将当前进程加到了各个监控描述符的wait queue中。现在这里又有了一个epoll自身的wait queue。这是为什么呢?
回答这个问题,需要我们再跳回ep_ptable_queue_proc——不记得这个函数的同学,请翻看前面的文章。这个函数调用init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);,将epoll当前进程的wait queue节点的回调函数设置为ep_poll_callback。对比epoll调用的init_waitqueue_entry函数,这个函数设置wait queue节点的回调函数为default_wake_function。

那么当监控文件描述符执行wakeup动作时,比如一个socket收到数据时,调用sk_data_ready->sock_def_readable->wake_up_interruptible_sync_poll->....最终会执行wait_queue节点的回调函数。对于epoll来说,即ep_poll_callback。

  1. static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *key)
  2. {
  3.     int pwake = 0;
  4.     unsigned long flags;
  5.     struct epitem *epi = ep_item_from_wait(wait);
  6.     struct eventpoll *ep = epi->ep;

  7.     spin_lock_irqsave(&ep->lock, flags);

  8.     /*
  9.      * If the event mask does not contain any poll(2) event, we consider the
  10.      * descriptor to be disabled. This condition is likely the effect of the
  11.      * EPOLLONESHOT bit that disables the descriptor when an event is received,
  12.      * until the next EPOLL_CTL_MOD will be issued.
  13.      */
  14.     if (!(epi->event.events & ~EP_PRIVATE_BITS))
  15.         goto out_unlock;

  16.     /*
  17.      * Check the events coming with the callback. At this stage, not
  18.      * every device reports the events in the "key" parameter of the
  19.      * callback. We need to be able to handle both cases here, hence the
  20.      * test for "key" != NULL before the event match test.
  21.      */
  22.     if (key && !((unsigned long) key & epi->event.events))
  23.         goto out_unlock;

  24.     /*
  25.      * If we are transferring events to userspace, we can hold no locks
  26.      * (because we're accessing user memory, and because of linux f_op->poll()
  27.      * semantics). All the events that happen during that period of time are
  28.      * chained in ep->ovflist and requeued later on.
  29.      */
  30.     if (unlikely(ep->ovflist != EP_UNACTIVE_PTR)) {
  31.         if (epi->next == EP_UNACTIVE_PTR) {
  32.             epi->next = ep->ovflist;
  33.             ep->ovflist = epi;
  34.         }
  35.         goto out_unlock;
  36.     }

  37.     /* If this file is already in the ready list we exit soon */
  38.     if (!ep_is_linked(&epi->rdllink))
  39.         list_add_tail(&epi->rdllink, &ep->rdllist);

  40.     /*
  41.      * Wake up ( if active ) both the eventpoll wait list and the ->poll()
  42.      * wait list.
  43.      */
  44.     if (waitqueue_active(&ep->wq))
  45.         wake_up_locked(&ep->wq);
  46.     if (waitqueue_active(&ep->poll_wait))
  47.         pwake++;

  48. out_unlock:
  49.     spin_unlock_irqrestore(&ep->lock, flags);

  50.     /* We have to call this outside the lock */
  51.     if (pwake)
  52.         ep_poll_safewake(&ep->poll_wait);

  53.     return 1;
  54. }
这个函数的注释相当清楚,可以清晰的知道每一行代码的用途。其中

  1. if (waitqueue_active(&ep->wq))
  2.         wake_up_locked(&ep->wq);
这两行代码,检测了epoll自身的wait queue上是否有等待的节点,如果有的话,就执行唤醒动作。对于epoll的使用者来说,如果用户态正阻塞在epoll_wait中,那么ep->wq一定不为空,这时就会被唤醒。将该进程移到就绪队列中。

这两篇文章基本上理清了epoll如何监控多个描述符及如何获得通知的过程。对于如何监控来说,还欠缺了epoll内部结构,如何保存的各个描述符,如何维护的信息等。不过这样的文章网上已经有了很多。也许以后我会针对这个问题,再写两篇文章吧。


目录
相关文章
|
4月前
|
API
epoll监听信号事件-signalfd
epoll监听信号事件-signalfd
27 0
|
4月前
epoll分析
epoll分析
|
网络协议
从内核角度分析 listen() 系统调用的 backlog 参数作用
从内核角度分析 listen() 系统调用的 backlog 参数作用
125 0
|
监控
驱动开发:内核监控进程与线程回调
在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。
330 0
驱动开发:内核监控进程与线程回调
|
消息中间件 测试技术 API
FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)
我们在前面单独介绍过FreeRTOS的任务通知和消息队列, 但是在FreeRTOS中任务间的通讯还有信号量,邮箱,事件组标志等可以使用 这篇文章就这些成员与消息队列和任务通知的关系进行说明分析
792 0
FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)
FreeRTOS事件组之事件组的创建(xEventGroupCreate())
FreeRTOS事件组之事件组的创建(xEventGroupCreate())
399 0
基于epoll封装的事件回调miniserver
epoll技术前两节已经阐述过了,目前主要做一下封装,很多epoll的服务器都是采用事件回调方式处理, 其实并没有什么复杂的,我慢慢给大家阐述下原理。 在networking.h和networking.cpp里,这两个文件主要实现了一些文件读写功能的回调函数 。
897 0
彻底学会使用epoll(一)——ET模式实现分析
注:之前写过两篇关于epoll实现的文章,但是感觉懂得了实现原理并不一定会使用,所以又决定写这一系列文章,希望能够对epoll有比较清楚的认识。是请大家转载务必注明出处,算是对我劳动成果的一点点尊重吧。
1754 0
彻底学会使用epoll(四)——ET的写操作实例分析
首先,看程序四的例子。 l 程序四 点击(此处)折叠或打开 #include unistd.
1058 0
彻底学会使用epoll(五)—— ET模式下的注意事项
彻底学会epoll(五)—— ET模式下的注意事项 ——lvyilong316 5.1 ET模式下的读写     经过前面几节分析,我们可以知道,当epoll工作在ET模式下时,对于读操作,如果read一次没有读尽buffer中的数据,那么下次将得不到读就绪的通知,造成buffer中已有的数据无机会读出,除非有新的数据再次到达。
2280 0