从Linux源码角度看Epoll,透过现象看本质( 四 )

ep_pollepoll_wait调用ep_poll,ep_poll实现如下:
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,int maxevents, long timeout){int res, eavail;unsigned long flags;long jtimeout;wait_queue_t wait;/** Calculate the timeout by checking for the "infinite" value ( -1 )* and the overflow condition. The passed timeout is in milliseconds,* that why (t * HZ) / 1000.*//* timeout是以毫秒为单位,这里是要转换为jiffies时间 。这里加上999(即1000-1),是为了向上取整 。*/jtimeout = (timeout < 0 || timeout >= EP_MAX_MSTIMEO) ?MAX_SCHEDULE_TIMEOUT : (timeout * HZ + 999) / 1000;retry:spin_lock_irqsave(&ep->lock, flags);res = 0;if (list_empty(&ep->rdllist)) {/** We don't have any available event to return to the caller.* We need to sleep here, and we will be wake up by* ep_poll_callback() when events will become available.*//* 没有事件,所以需要睡眠 。当有事件到来时,睡眠会被ep_poll_callback函数唤醒 。*/init_waitqueue_entry(&wait, current); /*将current进程放在wait这个等待队列中 。*/wait.flags |= WQ_FLAG_EXCLUSIVE;/* 将当前进程加入到eventpoll的等待队列中,等待文件状态就绪或直到超时,或被信号中断 。*/__add_wait_queue(&ep->wq, &wait);for (;;) {/** We don't want to sleep if the ep_poll_callback() sends us* a wakeup in between. That's why we set the task state* to TASK_INTERRUPTIBLE before doing the checks.*//* 执行ep_poll_callback()唤醒时应当需要将当前进程唤醒,所以当前进程状态应该为“可唤醒”TASK_INTERRUPTIBLE*/set_current_state(TASK_INTERRUPTIBLE);/* 如果就绪队列不为空,也就是说已经有文件的状态就绪或者超时,则退出循环 。*/if (!list_empty(&ep->rdllist) || !jtimeout)break;/* 如果当前进程接收到信号,则退出循环,返回EINTR错误 */if (signal_pending(current)) {res = -EINTR;break;}spin_unlock_irqrestore(&ep->lock, flags);/** 主动让出处理器,等待ep_poll_callback()将当前进程唤醒或者超时,返回值是剩余的时间 。* 从这里开始当前进程会进入睡眠状态,直到某些文件的状态就绪或者超时 。* 当文件状态就绪时,eventpoll的回调函数ep_poll_callback()会唤醒在ep->wq指向的等待队列中的进程 。*/jtimeout = schedule_timeout(jtimeout);spin_lock_irqsave(&ep->lock, flags);}__remove_wait_queue(&ep->wq, &wait);set_current_state(TASK_RUNNING);}/* Is it worth to try to dig for events ? *//** ep->ovflist链表存储的向用户传递事件时暂存就绪的文件 。* 所以不管是就绪队列ep->rdllist不为空,或者ep->ovflist不等于* EP_UNACTIVE_PTR,都有可能现在已经有文件的状态就绪 。* ep->ovflist不等于EP_UNACTIVE_PTR有两种情况,一种是NULL,此时* 可能正在向用户传递事件,不一定就有文件状态就绪,* 一种情况时不为NULL,此时可以肯定有文件状态就绪,* 参见ep_send_events() 。*/eavail = !list_empty(&ep->rdllist);spin_unlock_irqrestore(&ep->lock, flags);/** Try to transfer events to user space. In case we get 0 events and* there's still timeout left over, we go trying again in search of* more luck.*//* 如果没有被信号中断,并且有事件就绪,但是没有获取到事件(有可能被其他进程获取到了),并且没有超时,则跳转到retry标签处,重新等待文件状态就绪 。*/if (!res && eavail &&!(res = ep_send_events(ep, events, maxevents)) && jtimeout)goto retry;/* 返回获取到的事件的个数或者错误码 */return res;}ep_send_events()函数向用户空间发送就绪事件 。
ep_send_events()函数将用户传入的内存简单封装到ep_send_events_data结构中,然后调用ep_scan_ready_list()将就绪队列中的事件传入用户空间的内存 。用户空间访问这个结果,进行处理 。

从Linux源码角度看Epoll,透过现象看本质

文章插图
 

【从Linux源码角度看Epoll,透过现象看本质】


推荐阅读