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内部结构,如何保存的各个描述符,如何维护的信息等。不过这样的文章网上已经有了很多。也许以后我会针对这个问题,再写两篇文章吧。


目录
相关文章
|
3天前
【FreeRTOS】任务通知的使用
【FreeRTOS】任务通知的使用
|
3天前
|
移动开发
【FreeRTOS】事件组的使用
【FreeRTOS】事件组的使用
|
3天前
|
存储
FreeRTOS事件组
FreeRTOS事件组
16 0
|
3天前
|
API
epoll监听信号事件-signalfd
epoll监听信号事件-signalfd
29 0
|
5月前
|
监控 安全 API
7.1 Windows驱动开发:内核监控进程与线程回调
在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。
64 0
7.1 Windows驱动开发:内核监控进程与线程回调
|
安全 测试技术 API
5- FreeRTOS任务通知
5- FreeRTOS任务通知
|
消息中间件 存储 安全
探索FreeRTOS的功能:线程,消息队列,邮箱,信号量,互斥量,任务通知,延时,虚拟定时器
探索FreeRTOS的功能:线程,消息队列,邮箱,信号量,互斥量,任务通知,延时,虚拟定时器
1258 0
|
监控
驱动开发:内核监控进程与线程回调
在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。
334 0
驱动开发:内核监控进程与线程回调
|
消息中间件 测试技术 API
FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)
我们在前面单独介绍过FreeRTOS的任务通知和消息队列, 但是在FreeRTOS中任务间的通讯还有信号量,邮箱,事件组标志等可以使用 这篇文章就这些成员与消息队列和任务通知的关系进行说明分析
815 0
FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)