参考
–是否使用了mmap还存疑,源码分析中有看到mmap的函数,todo
源码
epoll_create
1 |
|
eventpoll_fops
1 |
|
总结
epoll_create
|->do_epoll_create
|->file->private_data = ep
|->fd_install
- epoll_create最主要创建一个eventpoll结构体
- epfd->file->private_date = eventpoll
- epfd->file->f_op = eventpoll_fops
- epoll_create的函数参数,在内核某个版本后已经没什么用了,只需要填入大于0的数即可。
- eventpoll的rdllink链表节点是epitem的rdllink成员,哪个epitem就绪了就把自己的rdllink加到双向链表rdllink中
- eventpoll的rbr红黑树节点是epitem的rbn成员,每个监控的epitem都要加到红黑树中
epoll_create执行后的主要结构:
图片来源:
1 |
|
epoll_event
1 |
|
struct epitem
1 | /* |
struct eventpoll
1 | /* |
epoll_ctl
1 |
|
ep_find
1 |
|
ep_pqueue
1 |
|
ep_insert
1 |
|
ep_item_poll
1 | /* |
poll_wait
1 |
|
ep_ptable_queue_proc
1 |
|
总结
epoll_ctl
|->do_epoll_ctl
|->ep_find
|->ep_insert
|->ep_item_poll
|->poll_wait(epi->ffd.file, &ep->poll_wait, pt)
|->ep_ptable_queue_proc
|->waitqueue中 //poll_table添加唤醒回调函数ep_poll_callback
|->add_wait_queue(将当前的waitqueue链入到epitem对应的等待列表)
|->ep_scan_ready_list
|->ep_remove
|->ep_modify
- ep_find的三个参数:epfd对应的eventpoll,epoll_ctrl参数中的fd对应的file结构,epoll_ctrl参数中的fd
- ep_find的逻辑:将fd转换为epoll_filefd格式,找rbr红黑树中每个epitem的ffd成员进行比较
- ep_insert新建一个epi(epitem),赋值然后插入红黑树
- ep_insert创建epq(ep_pqueue),(&epq.pt)->_qproc = ep_ptable_queue_proc poll队列回调函数
- ep_insert创建epq(ep_pqueue),(&epq.pt)->_key = epi->event.events(初始化后在ep_item_poll中赋值)
- poll_wait中执行回调函数ep_ptable_queue_proc
- ep_ptable_queue_proc新建一个pwq(eppoll_entry),&pwq->wait->func=ep_poll_callback(唤醒epoll_wait的函数)
- ep_ptable_queue_proc中pwq->base = epi
- ep_ptable_queue_proc中pwq->whead = &ep->poll_wait(file->poll使用的等待队列,和进程唤醒有关)
图片来源: 从linux源码看epoll
epoll_wait
epoll_wait
1 |
|
do_epoll_wait
1 | /* |
ep_poll
1 |
|
总结
epoll_wait
|->do_epoll_wait
|->ep_poll
|->send_events
|->fetch_events
- ep_poll根据超时时间执行不同策略
- ep_poll中判断就绪队列rdllist不为空,则发送就绪事件到用户空间send_events
- ep_poll没有事件则schedule_hrtimeout_range让出cpu,等待唤醒
- ep->ovflist的作用:记录处理过程中新到来的事件(不是很明确)
- 中断处理程序执行回调函数唤醒epoll_wait(我的猜测)
图片来源: 从linux源码看epoll
ep_send_events
ep_send_events
1 |
|
ep_scan_ready_list
1 | /** |
init_poll_funcptr
1 | static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc) |
list_for_each_entry_safe
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
ep_send_events_proc
1 |
|
struct poll_wqueues
1 | /* |
__pollwait(pollwait)
1 | /* |
struct poll_table_entry
1 | struct poll_table_entry { |
init_waitqueue_func_entry
1 | static inline void |
vfs_poll
1 |
|
总结
ep_send_events
|->ep_scan_ready_list
|->ep_send_events_proc(poll_table pt)
//pt->_key = epi->event.events;
|->ep_item_poll(epi, &pt, 1)
|->vfs_poll(epi->ffd.file, pt)
|->file->f_op->poll()
- ep_send_events只是知道epoll_wait被唤醒,还需要获取具体信息
- ep_send_events_proc遍历rdllink链表(全局的),直到其中某个节点的member成员=ep->rdllist,找到epi节点
- ep_item_poll执行epi->ffd.file->f_op->poll() 这边的poll即是file_poll(网络套接字则是tcp_poll),根据file本身的信息设置掩码(mask)等信息 & 上兴趣事件掩码
- ep_send_events_proc根据上一步的结果确认要不要将event放入用户空间
- ep_item_poll有2个分支,在两个2同阶段
图片来源: 从linux源码看epoll