epoll源码分析---sys_epoll_ctl()函数
一、sys_epoll_ctl()函数 源码和注释如下:
/* * 如果文件类型支持epoll并且有事件发生,发生的事件通过 * 参数key来传送,参见tcp_prequeue()函数中对wake_up_interruptible_poll() * 的调用。 * @wait: 调用ep_ptable_queue_proc()加入到文件中的唤醒队列时分配的 * eppoll_entry实例的wait成员的地址 * @mode:该参数在回调函数ep_poll_callback()中没有使用,其值为进程 * 睡眠时的状态 * @sync: 唤醒等待进程的标志 */static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *key){ int pwake = 0; unsigned long flags; struct epitem *epi = ep_item_from_wait(wait); struct eventpoll *ep = epi->ep; spin_lock_irqsave(&ep->lock, flags); /* * If the event mask does not contain any poll(2) event, we consider the * descriptor to be disabled. This condition is likely the effect of the * EPOLLONESHOT bit that disables the descriptor when an event is received, * until the next EPOLL_CTL_MOD will be issued. */ /* * epi->event.events中存储的是用户空间关心的事件,如果该成员 * 没有包含任何poll事件,则跳转到out_unlock处处理 */ if (!(epi->event.events & ~EP_PRIVATE_BITS)) goto out_unlock; /* * Check the events coming with the callback. At this stage, not * every device reports the events in the "key" parameter of the * callback. We need to be able to handle both cases here, hence the * test for "key" != NULL before the event match test. */ /* * 如果key不为NULL,也就是值不是0,但是用户关心的 * 事件并没有发生,则跳转到out_unlock处处理。参数key * 应该不会为0 */ if (key && !((unsigned long) key & epi->event.events)) goto out_unlock; /* * If we are trasfering events to userspace, we can hold no locks * (because we're accessing user memory, and because of linux f_op->poll() * semantics). All the events that happens during that period of time are * chained in ep->ovflist and requeued later on. */ /* * ep_scan_ready_list()是向用户空间传递事件的处理函数, * ep_scan_ready_list()函数执行时会将ovflist链表中的元素 * 暂存到一个临时变量中,然后将ovflist成员置为NULL, * 而EP_UNACTIVE_PTR的定义如下: * #define EP_UNACTIVE_PTR ((void *) -1L) * 因此(ep->ovflist != EP_UNACTIVE_PTR)成立时,正在向用户空间 * 传递事件。 * 如果当前正在向用户空间传递事件,则将 * 当前的事件对应的epitem实例加入到ovflist链表中。 */ if (unlikely(ep->ovflist != EP_UNACTIVE_PTR)) { /* * 如果epi->next不等于EP_UNACTIVE_PTR,则说明已经 * 添加到ovflist链表中,就不用再添加了 */ if (epi->next == EP_UNACTIVE_PTR) { epi->next = ep->ovflist; ep->ovflist = epi; } goto out_unlock; } /* If this file is already in the ready list we exit soon */ /* * 如果当前没有在向用户空间传递事件,用户 * 关心的事件已经发生,并且还没有加入到就绪 * 队列中,则将当前的epitem实例加入到就绪队列中。 */ if (!ep_is_linked(&epi->rdllink)) list_add_tail(&epi->rdllink, &ep->rdllist); /* * Wake up ( if active ) both the eventpoll wait list and the ->poll() * wait list. */ /* * 唤醒调用epoll_wait()函数时睡眠的进程。 */ if (waitqueue_active(&ep->wq)) wake_up_locked(&ep->wq); /* * 唤醒等待eventpoll文件状态就绪的进程 */ if (waitqueue_active(&ep->poll_wait)) pwake++;out_unlock: spin_unlock_irqrestore(&ep->lock, flags); /* We have to call this outside the lock */ /* * 唤醒等待eventpoll文件的状态就绪的进程 */ if (pwake) ep_poll_safewake(&ep->poll_wait); return 1;}该函数主要的功能是将被监视文件的等待事件就绪时,将文件对应的epitem实例添加到就绪队列中,当用户调用epoll_wait()时,内核会将就绪队列中的事件报告给用户