由于poll()
和select()
的局限,2.6内核引入了event poll(epoll)
机制。虽然稍微复杂,但是epoll解决了它们共有的基本性能问题,并增加了一些新的特性。poll()
和select()
每次调用都需要所有被监听的文件描述符。内核必须遍历所有被监视的文件描述符。当这个表变得很大时,成千上百的文件描述符,每次调用时的遍历就成为了明显的瓶颈。
创建一个新的epoll实例
使用epoll_create()或者epoll_cerate1()创建一个epoll上下文。这里epoll_cerate1()是epoll_cerate()的扩展版本。
|
|
调用成功后,epoll_create()
创建一个epoll
实例,返回与该实例关联的文件描述符。这个文件描述符和真正的文件没有关系,仅仅是为了后续调用使用epoll
而创建的。size参数告诉内核需要监听的文件描述符数目,但不是最大值。传递一个适当的近似值会带来性能的提升,但不需要给出确切的数字。出错时,返回-1,设置errno为下列值之一:
值 | 意义 |
---|---|
EINVAL | size不是正数 |
ENFILE | 系统达到打开文件数的上限 |
ENOMEN | 没有足够内存完成该次操作。 |
标准调用如下:
|
|
epoll_creat
e返回的文件描述符需要用close()
关闭。
控制 epoll
epoll_ctl
可以向指定的epoll上下文加入或删除文件描述符:
|
|
头文件<sys/epoll.h>
中定义了epoll event
结构体
|
|
epoll_ctl()
成功调用将关联epoll实例和epfd。参数op指定对fd要进行的操作。event参数描述epoll更具体的行为以下是参数op的有效值:
值 | 意义 |
---|---|
EPOLL_CTL_ADD | 把fd指定的文件添加到epfd指定的epoll实例监听集中,监听event中定义的事件 |
EPOLL_CTL_DEL | 把fd指定的文件从epfd指定的epoll监听集中删除。 |
EPOLL_CTL_MOD | 使用event改变在已有fd上的监听行为。 |
epoll_event
结构体中的event参数列出了在给定文件描述符上监听的事件。多个事件可以使用位或运算同时指定。以下是有效值:
值 | 意义 |
---|---|
EPOLLERR | 文件出错。即使不设置这个标志,这个事件也是被监听的。 |
EPOLLET | 使用边沿触发。默认是水平触发。 |
EPOLLHUP | 文件被挂起。即使不设置这个标志,这个事件也是被监听的。 |
EPOLLIN | 文件未阻塞,可读。 |
EPOLLONESHOT | 在一次事件产生被处理之后,文件不在被监听。必须不再被监听。必须使用EPOLL_CTL_MOD指定新的事件,以便重新监听文件。 |
EPOLLOUT | 文件未阻塞,可写。 |
EPOLLPRI | 高优先级的带外数据可读。 |
event_poll
中的data字段由用户使用。确认监听事件后,data会被返回给用户。通常将event.data.fd设定为fd,这样就可以知道那个文件描述符触发事件。
成功后,epoll_ctl()返回0.失败返回-1,并设置errno为下列值:
值 | 意义 |
---|---|
EBADF | epfd不是一个有效的epoll实例,或者fd不是有效文件描述符。 |
EEXIST | op为EPOLL_CTL_ADD,但是fd已经与epfd关联。 |
EINVAL | epfd不是一个epoll实例,epfd和fd相同,或者op无效。 |
ENOENT | op是EPOLL_CTL_MOD或者是EPOLL_CTL_DEL,但是fd没有与epfd关联。 |
ENOMEN | 没有足够内存完成进程的请求。 |
EPERM | fd不支持epoll。 |
在epfd实例中加入一个fd指定的监听文件,使用如下代码:
|
|
修改epfd实例中的fd上的一个监听事件,可以使用如下代码:
|
|
删除一个fd监听事件,可以使用如下代码:
|
|
等待Epoll事件
epoll_wait()
等待给定epoll实例关联的文件描述符上的事件:
|
|
对epoll_wait()的调用等待epoll实例epfd中的文件fd上的事件,时限为timeout毫秒。成功返回,struct epoll_event *events指向包含epoll_event结构体(该结构体描述了每个事件)的内存,且最多可以有maxevents个事件。这样可以根据events结构体来确定哪些fd触发了事件,从而做出相应的处理。返回值是事件数,出错返回1,并将errno设置为以下值:
值 | 意义 |
---|---|
EBADF | epfd是无效文件描述符 |
EFAULT | 进程对events指向的内存无写权限 |
EINTR | 系统调用在完成前被信号中断 |
EINVAL | epfd不是有效的epoll实例,或者maxevents小于等于0 |
如果timeout 为0.即使没有事件发生,调用也立即发生,此时调用返回0.如果timeout为-1,调用将一直等待到有事件发生。
当调用epoll_wait()返回,epoll_event结构体中的events数组描述了一次等待发生的事件,最多返回maxevents个事件。data字段包含了用户在调用epoll_ctl前的设置,如文件的句柄,用来区分那个文件所发生的事件。
一个完整的epoll_wait()
例子如下:
|
|
边沿触发时间和水平触发事件
EPOLL事件有两种模型 Level Triggered (LT) 和 Edge Triggered (ET):
LT(level triggered,水平触发模式)是缺省的工作方式,并且同时支持 block 和 non-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。
ET(edge-triggered,边缘触发模式)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,等到下次有新的数据进来的时候才会再次出发就绪事件。
man epoll 中的实例
setnonblocking()函数将socket文件设置为非阻塞,因为使用的是ET模式。do_use_fd()是对此文件做出一定的处理,如读写等。
|
|