epoll也是实现I/O多路复用的一种方法。epoll水平触发(level trigger,LT,LT为epoll的默认工作模式)与边缘触发(edge trigger,ET)两种工作模式。使用脉冲信号来解释LT和ET可能更加贴切。Level是指信号只需要处于水平,就一直会触发;而edge则是指信号为上升沿或者下降沿时触发。
LT:只要内核缓冲区有数据就一直通知,只要socket处于可读状态或可写状态,就会一直返回sockfd;
ET:只有状态发生变化才通知,只有当socket由不可写到可写或由不可读到可读,才会返回其sockfd;
注:(在添加EPOLLIN、EPOLLOUT事件的前提下。)
水平触发
1. 对于读操作
只要缓冲内容不为空,LT模式返回读就绪。
2. 对于写操作
只要缓冲区还不满,LT模式会返回写就绪。
边缘触发
1. 对于读操作
(1)当缓冲区由不可读变为可读的时候,即缓冲区由空变为不空的时候。
(2)当有新数据到达时,即缓冲区中的待读数据变多的时候。
(3)当缓冲区有数据可读,且应用进程对相应的描述符进行EPOLL_CTL_MOD
修改EPOLLIN
事件时。
2. 对于写操作
(1)当缓冲区由不可写变为可写时。
(2)当有旧数据被发送走,即缓冲区中的内容变少的时候。
(3)当缓冲区有空间可写,且应用进程对相应的描述符进行EPOLL_CTL_MOD
修改EPOLLOUT
事件时。
究竟触发了多少次呢?可以这样想:
(1)当数据量SIZE小于recv函数的接收长度len且小于系统缓冲SO_RCVBUF的时候:ET与LT都触发一次,recv执行一次。
(2)当数据量SIZE大于recv接收长度len但小于系统缓冲SO_RCVBUF的时候:LT触发SIZE/len次,ET触发一次。两者的recv函数执行SIZE/len。(一次ET触发可执行多次recv)
(3)当数据量SIZE大于recv接收长度len且大于系统缓冲SO_RCVBUF的时候:LT触发SIZE/len次,ET触发SIZE/SO_RCVBUF次。两者的recv函数都执行SIZE/len次。(同样一次ET触发执行了多次recv)
显然:
1、ET模式可以显著降低同一个Epoll事件的触发次数。
2、ET模式下同一文件描述符的同类事件有可能多次触发。如:系统读/写缓冲区溢出会分别导致同一个描述符的EPOLLIN/EPOLLOUT事件多次触发。
另外几点要知道:
(1)send和recv函数的len参数是每一次接受或发送的数据量的上限。
(2)ET模式下确实每次都终止于EAGAIN。recv的指定的接收长度较小的时候,ET模式下的一次触发会执行多次recv。直到某次recv操作把errno变成了EAGAIN为止。此时errno==EAGAIN条件成立。
(3)这种触发是系统自动完成的。某些程序在errno==EAGAIN的时候,又做了写epoll_ctl配置有点误导人。
(4)两种模式下的recv函数执行的次数都是一样的。
(5)具体执行次数还要考虑缓冲区门限之类的,并非这么简单。