EPOLL Linux内核源代码实现原理分析
作者 will.huang | 2012-08-05 15:44 | 类型 云计算, 互联网 | 9条用户评论 »
既然首席提到了select,我就分享一下最近写的关于epoll原理的文章,借花献佛了。哈哈 黄江伟 will.huang@aliyun-inc.com epoll的实现主要依赖于一个迷你文件系统:eventpollfs。此文件系统通过eventpoll_init初始化。在初始化的过程中,eventpollfs create两个slub分别是:epitem和eppoll_entry。 epoll使用过程中有几个基本的函数分别是epoll_create,epoll_ctl,epoll_wait。涉及到四个重要的数据结构: struct eventpoll , struct epitem, struct epoll_event ,struct eppoll_entry。 1、epoll_create和epoll_ctl epollcreate在调用ep_alloc通过anon_inode_getfd创建一个名字为“[eventpoll]”的eventpollfs文件描述符号并将file->private_data指定为指向前面生成的eventpoll。这样就将eventpoll和文件id关联。最后返回文件描述符id。 通过epoll_create生成一个eventpoll后,可以通过epoll_ctl提供的相关操作对eventpoll进行ADD,MOD,DEL操作。epoll_ctl有四个参数,分别是:int epfd(需要操作的eventpoll), int op(操作类型), int fd(需要被监视的文件), struct epoll_event *event(被监视文件的相关event)。epoll_ctl首先通过epfd的private_data域获取需要操作的eventpoll,然后通过ep_find确认需要操作的fd是否已经在被监视的红黑树中(eventpoll->rbr)。然后根据op的类型分别作ADD(ep_insert),MOD(ep_modify),DEL(ep_remove)操作。 首先分析ep_insert,ep_insert有四个参数分别为: struct eventpoll *ep(需要操作的eventpoll), struct epoll_event *event(epoll_create传入的event参数,当然得从user空间拷贝过来), struct file *tfile(被监视的文件描述符), int fd(被监视的文件id)。ep_insert首先从slub中分配一个epitem的对象epi。并初始化epitem的三个list头指针,rdllink(指向eventpoll的rdlist),fllist指向(struct file的f_ep_links),pwqlist(指向包含此epitem的所有poll wait queue)。并将epitem的ep指针,指向传入的eventpoll,并通过传入参数event对ep内部变量event赋值。然后通过ep_set_ffd将目标文件和epitem关联。这样epitem本身就完成了和eventpoll以及被监视文件的关联。下面还需要做两个动作:将epitem插入目标文件的polllist并注册回调函数;将epitem插入eventpoll的rbtree。 为了完成第一个动作,还需要一个数据结构ep_pqueue帮忙,ep_pqueue主要包含两个变量一个是epitem还有一个是callback函数(ep_ptable_queue_proc)相关的一个数据结构poll_table,ep_pqueue主要完成epitem和callback函数的关联。然后通过目标文件的poll函数调用callback函数ep_ptable_queue_proc。Poll函数一般由设备驱动提供,以网络设备为例,他的poll函数为sock_poll然后根据sock类型调用不同的poll函数如:packet_poll。packet_poll在通过datagram_poll调用sock_poll_wait,最后在poll_wait实际调用callback函数(ep_ptable_queue_proc)。 ep_ptable_queue_proc函数完成epitem加入到特定文件的wait队列任务。ep_ptable_queue_proc有三个参数:struct file *file(目标文件), wait_queue_head_t *whead(目标文件的waitlist), poll_table *pt(前面生成的poll_table)。在函数中,引入了另外一个非常重要的数据结构eppoll_entry。eppoll_entry主要完成epitem和epitem事件发生时的callback(ep_poll_callback)函数之间的关联,并将上述两个数据结构包装成一个链表节点,挂载到目标文件file的waithead中。 这里还得完成两个动作,首先将eppoll_entry的whead指向目标文件的waitlist(传入的参数2),然后初始化base变量指向epitem,最后通过add_wait_queue将epoll_entry挂载到目标文件的waitlist。完成这个动作后,epoll_entry已经被挂载到waitlist,然后还有一个动作必须完成,就是将eppoll_entry挂载到epitem的pwqlist上面。现在还剩下一个动作,就是将epitem的fllink链接到目标文件的f_ep_links上,这部分工作将在poll函数返回后在ep_insert中完成。当然ep_insert除了完成这个动作外,还会完成前面提到的第二步,epitem插入eventpoll的rbtree。完成以上动作后,将还会判断当前插入的event是否刚好发生,如果是,那么做一个ready动作,将epitem加入到rdlist中,并对epoll上的wait队列调用wakeup。 到此为止基本完成了epoll_create以及epoll_ctl最重要的ADD函数的工作介绍。下面进入epoll_wait函数介绍。 2、epoll_wait ep_send_events通过ep_scan_ready_list对ready_list进行扫描,由于现在在对ready_list进行操作,这个时候必须保证rdlist数据的一致性,如果此时又有新的event ready,那么我们必须提供临时的存储空间,eventpoll提供了一个ovflist用来存储这种event。ep_send_events获取了rdlist后通过ep_send_events_proc完成真正的转发工作。完成转发后,ep_send_events还需要去判断ovflist,如果ovflist中有events,那么还需要将这些events转移到rdlist中。 ep_send_events_proc扫描rdlist从头上面拿出epitem,然后调用epollfs的poll函数(ep_eventpoll_poll),判断拿出来的那个events是否真的已经ready(这部分比较难理解,没怎么看懂)。如果ready,那么将数据封装到uevent里面,同事这里还需要判断epitem的类型是否是Level Triggered如果是,那么还需要把event再次插入队列尾部。 3、ep_poll_callback ep_poll_callback函数首先会判断是否rdlist正在被使用(通过ovflist是否等于EP_UNACTIVE_PTR),如果是那么将epitem插入ovflist。如果不是那么将epitem插入rdlist。然后调用wake_up函数唤醒epitem上wq的进程。这样就可以返回到epoll_wait的调用者,将他唤醒。 | |
雁过留声
“EPOLL Linux内核源代码实现原理分析”有9个回复
排版!!!
如果能配个图就更好了
解释函数画一个流程图更直观一些.
“当socket数据ready,终端会调用相应的接口函数”
是“中断”吧?
一个图都没有,排版惨不忍睹。
看这个文章还不如看源码轻松 =。=
也不能全怪作者。。。如果你在湾区写过文章的话。。。
首席最近没工夫搞排版了。估计在为alexa奔命呢
昏了,,,感觉像把原代码口述出来,,,还不如贴代码。。。
基本是垃圾。这种复述源代码的事情有什么意义。重要的是原理而不是code本身。每一行代码分析的再透彻又有什么用,换个地方又不认识了。sigh~~
ls两位,请理解epoll的原理后再来看这篇文章,从你俩的发言来看,这篇文章的定位不是你俩