一. epoll用户态使用规范

epoll有2种工作方式:LT和ET。

LT(level triggered,水平触发)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。

ET (edge-triggered,边缘触发)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。

epoll相关的系统调用有3个:epoll_create, epoll_ctl和epoll_wait。在头文件<sys/epoll.h>

1. int epoll_create(int size)

创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epoll的事件注册函数,注册要监听的事件类型,比如某个被监控的fd被读了,触发epoll_wait.

OP的值有:

epoll_event结构体则是:

events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)

用于轮询I/O事件的发生,其中epfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait函数操作成功之后,events里面将储存所有的读写事件。maxevents是当前需要监听的所有socket句柄数。最后一个timeout参 数指示 epoll_wait的超时条件,为0时表示马上返回;为-1时表示函数会一直等下去直到有事件返回;为任意正整数时表示等这么长的时间,如果一直没有事 件,则会返回。一般情况下如果网络主循环是单线程的话,可以用-1来等待,这样可以保证一些效率,如果是和主循环在同一个线程的话,则可以用0来保证主循 环的效率.epoll_wait返回之后,应该进入一个循环,以便遍历所有的事件。

二. epool在内核中的代码分析

代码版本:linux-3.16.37-git.

1. epoll_create

SYSCALL_DEFINE1(epoll_create, int, size)和SYSCALL_DEFINE1(epoll_create1, int, flags)没什么区别,epoll_create的入参的size值在内核就直接被废掉了,变成了epoll_create1(0)。在epoll_create1下,引入了一些结构体eventpoll和epitem:

先不管这些结构体,先看eventpoll结构体的初始化

上面仅仅提到fd的获取而已,需要注意的是eventpoll_fops

2. epoll_ctl

先看ep_insert函数:

ep_create_wakeup_source注册了一个唤醒源...

太复杂了,看不下去了。

3.epoll_wait

SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,int, maxevents, int, timeout)

在ep_poll下,

4.ep_poll_callback的通知

在看一下ep_pqueue队列

在ep_eventpoll_poll下,调用了poll_wait(file, &ep->poll_wait, wait),即是:

if (p && p->_qproc && wait_address)

p->_qproc(filp, wait_address, p);

而_qproc则为ep_ptable_queue_proc,其下的wait_queue_head_t就来自于&ep->poll_wait,将ep_poll_callback加到队列里面

init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);

等ep_poll_callback被调度后,通过唤醒源将ep_poll唤醒。

 


epoll的linux内核工作机制来自于OenHan

链接为:http://oenhan.com/epoll-linux-kernel

发表评论