EPOLL之内核实现

 

Epoll也是通过文件描述符的方式控制,因此,epoll被设计成虚拟文件系统” eventpollfs”

 

数据结构:

struct eventpollepoll文件的控制结构,存储在file-> private_data

ep->wq:   等待ep事件发生的wait queue。通过sys_epoll_wait(…)加入的wait queue

ep->poll_wait: 等等ep文件接口事件发生的wait queue,通过ep_eventpoll_poll(…)加入的wait queue。一般情况下,使用eq->wq较普遍!

 

struct epitem;  描述一个被监视文件的相关信息。Epitem可以在eventpoll的几个链中,分别为:

epi->rbn:对应ep->rbr,表示ep监视文件表中的一个结点。

epi->rdllink:对应ep->rdllist,表示ep监视文件表中已经readyepitem链表。

epi->fllink: 对应tfile-> f_ep_links,用于关闭tfile时,从ep中释放相应的epitem

epi->pwqlist: epitem 加入的wait queue的列表。

epi->txlink: transfer list,用户调用epoll_wait,将ep的已经ready的文件中转化为用户的event集合时使用。

 

struct eppoll_entry; 描述poll hook,通过这个结构可以把epitem加入被监视文件的wait queue

 

EPOLL模块初始化

eventpoll_init():创建epitem cacheepoll_entry cache,注册并挂载eventpollfs

 

接口函数:

创建一个epoll文件的函数

long sys_epoll_create(int size)

1. 申请eventpoll结构ep;

2. 创建一个epoll inode, 创建一个dentry,获取get_unused_fdget_empty_filp获取file, 然后把file->private_data = ep; 然后调用fd_install(fd, file)安装epoll,最后返回文件描述符epollfd

 

sys_epoll_ctl (int epfd, int op, int fd, struct epoll_event __user *event)

1.       通过epfd找到eventpoll结构ep, 通过fd找到需要查询的文件tfile, 并确认tfile有支持poll函数。

2.       通过红黑树查找被监视的tfile是否在ep-> rbr中。如果在,则返回其监视fdepitem

3.       看操作类型op,决定做不同的事情:

a)       如果是EPOLL_CTL_ADD,则调用ep_insert()生成亲的epitem,被设置epi->event = *event; 然后将epitem加入ep->rbr红黑树中。

调用tfile->f_op->poll(tfile, &epq.pt),将epitem加入tfilewaitquque中,注册唤醒回调函数:ep_poll_callback()。将epi->fllink加入tfile->f_ep_links链表中。

 

获取event,如果有感兴趣的事件,则将调用list_add_tail(&epi->rdllink, &ep->rdllist)epitem加入到ep-> rdllist。如果ep->wq有等待进程,则唤醒等待进程。

注:这里设置的epi->event除了用户设置的事件外,还自动添加了POLLERR | POLLHUP事件,因此用户不需要额外设置这两个事件。

 

b)      如果是EPOLL_CTL_modify,则调用ep_modify(),更新epi->event.events,然后获取tfilepoll事件,(注意:这里不会加入被监视的事件wait queue),如果有感兴趣的事件,则调用list_add_tail(&epi->rdllink, &ep->rdllist);

 

c)      如果是EPOLL_CTL_DEL,调用ep_remove(),从被监视文件的wait queue中删除,删除epi->fllink,把epitem从红黑树中删除,如果已经在ep的感兴趣队列,则从感兴趣队列中删除它。

 

long sys_epoll_wait(int epfd, struct epoll_event __user *events, int maxevents, int timeout):

1.  通过epfd找到ep, 然后调用ep_poll()做以下所有事情:

2.  如果ep->rdllist为空,或者timeout=0,则将current加入到eq->wq中,然后调用schedule_timeout(timeout)进行进程切换。

3.  判断ep-> rdllist列表是否为空,如果还是为空,说明epoll_wait超时,直接返回。否则说明已经有文件处于ready状态,调用ep_events_transfer(),将ep->rdllistepitem加入到用户态的event集合中。ep_events_transfer函数第4步说明。

4.  ep_events_transfer()函数说明: 

A)     epitemepready list从移走,放到一个临时的transfr list

B)      然后对每一个在transfer list中的epitem,判断是否有用户感兴趣的事件,有加入用户events中。

C)     然后将epitemtransfer list中移除,如果epitem被上报的事件有用户感兴趣的事件,且不是ET模式,则加epitem重新加入epready list

 

当某个监视文件有事件发生时,会调用wake_up_xxx (),这时,ep_poll_callback被调用,处理如下:

 

被监控文件事件ready时,会调用wake_up_xxx (),对于epoll接口来说, ep_poll_callback ()被调用:首先将epitem加入ep-> rdllist, 然后wakup 被阻塞的进程ep->wq。被epoll_wait()函数阻塞的进程被唤醒后,调用ep_events_transfer()将用户感兴趣的事件返回给应用程序。

 

QETLT模式的区别?

答:见ep_events_transfer函数说明的C)说明。

 

Qepollready list怎么区分in事件和out事件?虽然用户感兴趣的可能是读事件,但是对于套接字来说,加入的是sk->sk_sleep。后续无论是读事件还是写事件上报,都会唤醒调用epoll wait()的进程,从而促发检查。换句话说,用户可能只关心读事件,但socket可写事件也会唤醒调用epoll_wait()的进程,从而进行无效的检查。

改进点:sk->sk_sleep分为sk_rsleep, sk_wsleep,不同的事件hook注册到不同的wait queue中。

 

关闭epoll接口:ep_eventpoll_close(struct inode *inode, struct file *file)

略。

 

你可能感兴趣的:(struct,list,File,callback,hook,events)