poll学习笔记


epoll

先参考这个:https://evian-zhang.github.io/introduction-to-linux-x86_64-syscall/src/filesystem/epoll_create-epoll_wait-epoll_ctl-epoll_pwait-epoll_create1.html

epoll_create

需要知道epoll_create和epoll_create1的功能是在内核空间返回epoll实例,并将相应的文件描述符返回到用户态;

关于epoll_create的具体实现原理看这篇:https://zhuanlan.zhihu.com/p/463541274

直接上源码:https://elixir.bootlin.com/linux/v5.19/source/fs/eventpoll.c#L2008

可以看到两个系统调用最终都直接调用了do_epoll_create函数,只不过是flags不一样,epoll_create的flags是默认的0,而epoll_create1的flags则是有用户传入的。

下面看do_epoll_create函数:

可以看到首先就是通过ep_alloc分配一个eventpoll类型的结构体,然后搞一个可用文件描述符和匿名inode安装上:

eventpoll结构体定义如下:https://elixir.bootlin.com/linux/v5.19/source/fs/eventpoll.c#L177

eventpoll结构有三个字段是比较重要的,分别是:wqrdllistrbr

  • wq用于保存有哪些进程在等待这个epoll返回。
  • rdllist用于保存可读写的文件。
  • rbr用于建立一个快速查找文件句柄的红黑树。

epoll_ctl

创建epoll实例大致的过程我们已经知道了,但是这个实例后续又该怎么用呢?

先看epoll_ctl系统调用的入口:https://elixir.bootlin.com/linux/v5.19/source/fs/eventpoll.c#L2183

到do_epoll_ctl函数:https://elixir.bootlin.com/linux/v5.19/source/fs/eventpoll.c#L2033

先梳理结构:

这里的ep是一个eventpoll结构体,可以看到这就是从我们epoll_create创建的文件中直接取private_data,这也就是之前创建的epoll实例;

直接看op==EPOLL_CTL_ADD的情况,先进行相关的检查(直接跳过😊):

真正的处理在这里:

可以看到主要就是调用了ep_insert函数:

其主要作用就是分配一个新的epitem并初始化,然后插入到红黑树中;

之后通过init_poll_funcptr函数将poll_table的_qproc设置成ep_ptable_queue_proc函数的地址:

之后会调用ep_item_poll:

这里会有如下调用链:

ep_item_poll

​ vfs_poll //通过is_file_epoll(file, pt) == 0进入

​ file->f_op->poll //走file的poll指针了

​ timerfd_poll //下面我们通过timerfd的文件来分析😊

​ poll_wait

​ poll_table->_qproc

​ ep_ptable_queue_proc

在ep_table_queue_proc函数中,https://elixir.bootlin.com/linux/v5.19/source/fs/eventpoll.c#L1239

主要分五个步骤进行:

第一步,利用container_of宏得到epq,进而解析出epi;

第二步,分配一个新的eppoll_entry;

第三步,给新分配的epoll_entry的wait成员初始化;

第四步,将epoll_entry的wait加入到wqh的链表中;

第五步,将epoll_entry加入到epi的pwqlist链表中;

总结

首先epoll_create创建epoll实例(eventpoll结构体)

然后在epoll_ctl,op==EPOLL_CTL_ADD的情况下进入到ep_insert中,在这里在栈上分配一个ep_pqueue,然后堆上分配其epitem,然后插入到eventpoll实例的红黑树中;

之后将poll_table的函数指针进行初始化,并通过ep_item_poll以及后续步骤最终调用到这个函数指针;

在这个处理函数中,分配一个新的eppoll_entry结构,其wait.func初始化为poll的处理函数(这个后续事件触发的时候会被调用),然后将eppoll_entry插到epitem的链表中,将eppoll_entry的watch成员挂到timerfd_ctx.wqh上;(事件触发后会将这个wqh上的所有的watch的func一次进行调用)

参考

https://evian-zhang.github.io/introduction-to-linux-x86_64-syscall/src/filesystem/epoll_create-epoll_wait-epoll_ctl-epoll_pwait-epoll_create1.html

https://zhuanlan.zhihu.com/p/463541274


文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录