epoll详解
一、epoll内核调用
1、内核事件表
1.epoll_create函数
epoll是Linux特有的I/O复用函数,epoll使用一组函,而不是一个数。
epoll把文件描述符fd放到了内核的一个事件表里,所以无须每次调用重传fd
epoll需要一个额外的fd来唯一的标识内核中的事件表:epoll_create函数
size参数不起作用,仅告诉内核事件表的大小
epoll_create函数返回的文件描述符将作用其他epoll系统调用的第一个参数,用来指定要访问的内核事件表
2.epoll_ctl函数
用来操作epoll的内核事件表:
fd是要操作的文件描述符,op是指定操作类型,操作类型有三种:
EPOLL_CTL_ADD:往事件表中注册fd上的事件
EPOLL_CTL_MOD:修改fd上的注册事件
EPOLL_CTL_DEL:删除fd上的注册事件
event参数指定事件,是epoll_event结构指针类型,其结构体定义如下:
events成员描述事件类型,epoll的事件类型基本和poll是一致的,但是在其基础上有额外的两个事件类型:EPOLLET和EPOLLONESHOT。
data成员用于存储用户数据,epoll_data_t定义如下:
epoll_data_t 是一个联合体,其四个成员使用最多的是fd,指定文件操作符。
ptr用以指定与fd相关的用户资源。
epoll_ctl 成功返回0,失败返回-1 并设置errno。
2、epoll_wait函数
1.epoll_wait函数
epoll系列系统调用的主要接口:epoll_wait函数
epoll_wait函数在一段超时时间等待一组文件描述符上的事件:
成功时返回就绪的文件描述符膈俞,失败返回-1并设置errno。
timeout参数指定epoll的超时值
maxevents参数指定最多监听多少个事件,必须大于0
2.epoll_wait函数的检测过程
epoll_wait检测到事件
就将所有就绪的事件从内核事件表中复制到events指向的数组中,此数组仅用于检测就绪事件,poll、select的数组参数即用于传入用户注册的事件,又用于输出内核检测的就绪事件。
epoll_wait极大地提高 了应用程序索引就绪文件描述符的效率
3、LT和ET模式
epoll对文件描述符的操作有两种模式:LT(电平触发)模式 和 ET(边沿触发)模式
1.LT(Level Trigger)模式
LT模式是默认的工作模式,这种模式下的epoll相当于一个效率较高poll
2.ET(Edge Trigger)模式
ET模式是epoll的高效模式,当epoll内核事件表中注册一个文件描述符上的EPOLLET事件时,epoll将以ET模式来操作。
3.LT和ET的效率
使用LT模式时,当epoll_wait检测到fd上有事件并通知应用程序后,应用程序可以不立即处理该事件,这样下一次调用epoll_wait时,epoll_wait还会向告诉应用程序通知此事件,直到事件被处理;
使用ET模式时,epoll_wait检测到fd上有事件并通知应用程序后,应用程序必须立即处理此事件,ET模式很大程度降低了同一个epoll事件被重复触发的次数,
由此可见ET效率和LT高。
每个使用ET模式的fd都应该是非阻塞的。如果fd是阻塞的那么读/写操作会处于饥渴状态(因没有后续的事件而一直处于阻塞)。
4、EPOLLONESHOT事件
即使使用ET模式,socket上某个事件还是可能会被触发多次,由此在并发程序会引起一个问题:
一个线程在读取完某个socket上的数据后开始处理这些数据,而在数据的处理过程中该socket上又有新数据可读,此时另外一个线程被唤醒来读取这些新的数据,于是就出现了两个线程读取一个socket的情况。
EPOLLONESHOT事件实现的就是 :一个socket连接在任一时刻都只被一个线程读取。
注册过EPOLLONESHOT事件的fd,操作系统最多触发其上注册的一个事件(可读、可写或异常),且只触发一次,除非使用epoll_ctl重置fd上注册的EPOLLONESHOT事件(每次注册完EPOLLONESHOT事件的socket被处理完毕,该线程应该立即重置EPOLLONESHOT事件,从而让其他工作线程有机会继续处理这个socket)。