头文件
#include <sys/epoll.h>
epoll概念
epoll是Linux特有的I/O复用函数。它在实现和使用上与select、poll有很大差异。epoll使用一组函数完成任务而非单个函数。epoll把用户关心的文件描述符上的事件放在内核中的一个事件表中,无须像select和poll那样每次调用都要重复传入文件描述符或事件集。但epoll需使用一个额外的文件描述符,来标识内核中的这个时间表。
使用情况:
epoll每次添加文件描述符的时候都需要执行一个系统调用。系统调用的开销是很大的,而且在有很多短期活跃连接的情况下,由于这些大量的系统调用开销,epoll可能会慢于select和poll。
select使用线性表描述文件描述符集合,文件描述符有上限;poll使用链表来描述;epoll底层通过红黑树来描述,并且维护一个ready list,将事件表中已经就绪的事件添加到这里,在使用epoll_wait调用时,仅观察这个list中有没有数据即可。
select和poll的最大开销来自内核判断是否有文件描述符就绪这一过程:每次执行select或poll调用时,它们会采用遍历的方式,遍历整个文件描述符集合去判断各个文件描述符是否有活动;epoll则不需要去以这种方式检查,当有活动产生时,会自动触发epoll回调函数通知epoll文件描述符,然后内核将这些就绪的文件描述符放到之前提到的ready list中等待epoll_wait调用后被处理。
select和poll都只能工作在相对低效的LT模式下,而epoll同时支持LT和ET模式。
综上,当监测的fd数量较小,且各个fd都很活跃的情况下,建议使用select和poll;当监听的fd数量较多,且单位时间仅部分fd活跃的情况下,使用epoll会明显提升性能。
epoll_create
该函数生成一个epoll专用的文件描述符,用来存放你想关注的文件描述符,监视其是否发生以及发生了什么事件。
@param size:现在不起作用,只是给内核一个提示,告诉它事件表需要多大。
int epoll_create(int size)
//返回生成的文件描述符,指示要访问的内核事件表
epoll_ctl
用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。
@param epfd:作用的目标epoll文件描述符
@param op:操作类型
@param fd:需要监听的文件描述符
@param event:需要监听的事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
event:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
fd值 | 功能 |
---|---|
EPOLL_CTL_ADD | 往事件表中注册fd上的事件 |
EPOLL_CTL_MOD | 修改fd上的注册事件 |
EPOLL_CTL_DEL | 删除fd上的注册事件 |
events值 | 功能 |
---|---|
EPOLLIN | 触发该事件,表示对应的文件描述符上有可读数据。(包括对端SOCKET正常关闭) |
EPOLLOUT | 触发该事件,表示对应的文件描述符上可以写数据 |
EPOLLPRI | 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来) |
EPOLLERR | 表示对应的文件描述符发生错误 |
EPOLLHUP | 表示对应的文件描述符被挂断 |
EPOLLET | 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的 |
EPOLLONESHOT | 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 |
epoll_wait
等待事件的产生,超过timeout时间还没事件发生,则超时
@param epfd: epoll文件描述符
@param events: 检测到就绪事件就从内核事件表中复制到此数组
@param maxevents: 指定最多监听多少个事件
@param timeout: 指定超时值,单位毫秒。为-1:epoll调用一直阻塞,直到某事件发生;为0:epoll调用立刻返回
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
//成功时返回就绪的文件描述符的个数,失败返回-1并设置errno
ET与LT模式
ET:events值取EPOLLONESHOT.
LT:events值取EPOLLET.
LT(电平触发):类似select,LT会去遍历在epoll事件表中每个文件描述符,来观察是否有我们感兴趣的事件发生,如果有(触发了该文件描述符上的回调函数),epoll_wait就会以非阻塞的方式返回。若该epoll事件没有被处理完(没有返回EWOULDBLOCK),该事件还会被后续的epoll_wait再次触发。
ET(边缘触发):ET在发现有我们感兴趣的事件发生后,立即返回,并且sleep这一事件的epoll_wait,不管该事件有没有结束。
在使用ET模式时,必须要保证该文件描述符是非阻塞的(确保在没有数据可读时,该文件描述符不会一直阻塞);并且每次调用read和write的时候都必须等到它们返回EWOULDBLOCK(确保所有数据都已读完或写完)。