epoll
2021-04-22 20:30:14 0 举报
多路复用源码流程梳理
作者其他创作
大纲/内容
eavail = !list_empty(&ep->rdllist) || ep->ovflist != EP_UNACTIVE_PTR;
校验参数合法性,将用户态数据拷贝到内核态
进入for(;;)循环
获取epfd对应的eventpoll文件的file实例(file结构是在epoll_create中创建的)
如果就绪队列不为空,也就是说已经有文件的状态就绪或者超时,则退出循环。
执行ep_poll_callback()唤醒时应当需要将当前进程唤醒,所以当前进程状态应该为“可唤醒”TASK_INTERRUPTIBLE: set_current_state(TASK_INTERRUPTIBLE);
为空
goto retry;
switch case判断需要处理的操作op(插入、删除、修改等)
ep_ptable_queue_proc函数设置了等待队列的ep_poll_callback回调函数。所以在设备硬件数据到来时,硬件中断处理函数中会唤醒该等待队列上等待的进程时,会调用唤醒函数ep_poll_callback且引入了另外一个非常重要的数据结构eppoll_entry。eppoll_entry主要完成epitem和epitem event发生时的callback(ep_poll_callback)函数之间的关联。首先将eppoll_entry的whead指向fd的设备等待队列(同select中的wait_address),然后初始化eppoll_entry的base变量指向epitem,最后通过add_wait_queue将epoll_entry挂载到fd的设备等待队列上。完成这个动作后,epoll_entry已经被挂载到fd的设备等待队列。
如果有进程等待eventpoll文件本身的事件就绪,则增加临时变量pwake的值,pwake的值不为0时,在释放lock后,会唤醒等待进程。
struct eventpoll { spin_lock_t lock; //对本数据结构的访问 struct mutex mtx; //防止使用时被删除 wait_queue_head_t wq; //sys_epoll_wait() 使用的等待队列 wait_queue_head_t poll_wait; //file->poll()使用的等待队列 struct list_head rdllist; //事件满足条件的链表 struct rb_root rbr; //用于管理所有fd的红黑树(树根) struct epitem *ovflist; //将事件到达的fd进行链接起来发送至用户空间}
如果没有被信号中断,并且有事件就绪,但是没有获取到事件(有可能被其他进程获取到了),并且没有超时,则跳转到retry标签处,重新等待文件状态就绪。
调用anon_inode_getfd 新建一个file instance,也就是epoll可以看成一个文件(匿名文件)。且将ep对象赋值到创建的epoll fd file中。因此我们可以看到epoll_create会返回一个fd。epoll所管理的所有的fd都是放在一个大的结构eventpoll(红黑树)中。
...
return 0;
ep_alloc(&ep);
若刚才f_op->poll的返回值revents中标识的事件有用户event关心的事件发生,并且epi的ready队列中有数据。
检查用户空间传入的events指向的内存是否可写
fd = anon_inode_getfd(\"[eventpoll]\
struct ep_queue{ poll_table pt; struct epitem *epi;}
分配一个epitem结构体来保存每个加入的fdepq.epi = epi;
初始化struct epitem *epi;struct ep_pqueue epq;
如果当前进程接收到信号,则退出循环,返回EINTR错误
条件块执行完
防止重复添加
插入
ep->ovflist链表存储的向用户传递事件时暂存就绪的文件。所以不管是就绪队列ep->rdllist不为空,或者ep->ovflist不等EP_UNACTIVE_PTR,都有可能现在已经有文件的状态就绪。ep->ovflist不等于EP_UNACTIVE_PTR有两种情况:· 一种是NULL,此时可能正在向用户传递事件,不一定就有文件状态就绪;· 一种情况是不为NULL,此时可以肯定有文件状态就绪;
移除等待队列中的对象,并设置状态为RUNNING
取出epoll_create创建的eventpoll 对象
检查maxevents参数:if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) return -EINVAL;
这两个函数将ep_ptable_queue_proc注册到epq.pt中的qproc:typedef struct poll_table_struct { poll_queue_proc qproc; unsigned long key;}poll_table;· 若fd是套接字,f_op为socket_file_ops,poll函数是sock_poll();· 若是TCP套接字的话,进而会调用到tcp_poll()函数。此处调用poll函数是查看当前文件描述符的状态,存储在revents中。在poll的处理函数(tcp_poll())中,会调用sock_poll_wait(),在sock_poll_wait()中会调用到epq.pt.qproc指向的函数,也就是ep_ptable_queue_proc()。
觉得不错可以关注我的个人公众号:哈利路亚儿
为ep分配内存并进行初始化。(ep为eventpoll结构)
通过检查epfd对应的文件操作是不是eventpoll_fops 来判断epfd是否是一个eventpoll文件。如果不是则返回EINVAL错误。
没有事件,所以需要睡眠。当有事件到来时,睡眠会被ep_poll_callback函数唤醒。
ep_poll_callback函数:主要的功能是将被监视文件的等待事件就绪时,将文件对应的epitem实例添加到就绪队列中,当用户调用epoll_wait()时,内核会将就绪队列中的事件报告给用户。
timeout是以毫秒为单位,这里是要转换为jiffies时间
将当前进程加入到eventpoll的等待队列中,等待文件状态就绪或直到超时,或被信号中断。
return fd;
break
return error;
epoll_create(int size)
返回获取到的事件的个数或者错误码return res;
判断满足就绪条件的链表是否为空list_empty(&ep->rdllist)
0 条评论
下一页