epoll触发事件的分析

我们知道,可以将套接字放入到epoll中监听,当套接字上有事件发生时,epoll就会通知我们。epoll机制可以监听套接字上的以下6种事件:
  • EPOLLIN:套接字可读
  • EPOLLOUT:套接字可写
  • EPOLLRDHUP:对端关闭了套接字,或者对端关闭了写
  • EPOLLPRI:套接字上有紧急数据到达
  • EPOLLHUP:对端挂断了套接字

那么,什么情况下会在套接字上触发以上6种事件呢?当我们监听套接字上的EPOLLIN + EPOLLRDHUP事件时,总结如下:
  • 监听套接字上有连接到达时,监听套接字上将触发EPOLLIN(可读)事件;
  • 连接套接字上有数据到达时,套接字上将触发EPOLLIN(可读)事件;
  • 如果对端发送数据后立即关闭套接字或shutdown写,则本端在收到最后一个数据包时会触发EPOLLIN+EPOLLRDHUP事件;
  • 对端按ctr+c杀死进程时,本端的套接字上将触发EPOLLIN+EPOLLRDHUP事件;
  • 对端shutdown写,本端的套接字上触发EPOLLIN+EPOLLRDHUP事件;
  • 本端调用shutdown关闭写,本端的套接字上不产生事件;
  • 对端shutdown读,本端的套接字上不触发任何事件;
  • 本端调用shutdown关闭读,本端的套接字上将触发EPOLLIN+EPOLLRDHUP事件;
  • 对端shutdown读写,本端的套接字上触发EPOLLIN+EPOLLRDHUP事件;
  • 本端shutdown读写,本端的套接字上触发EPOLLIN+EPOLLHUP+EPOLLRDHUP事件;
  • 当关闭(close)套接字时,内核会自动将套接字描述符从epoll中删除,因此本端不会再触发任何事件;
  • 当对端关闭(close)套接字(该套接字只被一个进程引用)时,其效果与shutdown读写一样,本端的套接字上触发EPOLLIN+EPOLLRDHUP事件;
  • 当对端关闭(close)或shutdown写后,本端的套接字上触发EPOLLIN+EPOLLRDHUP事件,并且这两个事件将一直存在epoll中。也就是说,在水平触发模式下,只要对端shutdown或关闭套接字了,本端就会不断触发EPOLLIN+EPOLLRDHUP事件。另外如果是边沿触发模块,如果该套接字上后续再有其他事件触发,这两个事件也会一起触发。
  • 在调用close关闭套接字时,如果该套接字描述符在内核中的引用计数为1,内核将shutdown读写,如果如果该套接字描述符在内核中的引用计数大于1(比如创建一个套接字后又fork出一个子进程),close只是递减引用计数后立即返回,这样另一个进程还可以继续使用这个套接字。这种情况下,如果要关闭这条连接,应当调用shutdown,此时会给对端发送FIN信号。
  • 如果对端已close套接字(或shutdown了读写),本端继续在这个套接字上发送数据,虽然数据可以正常发送出去,但本端会收到对端发来的RST信号,从而触发本端的EPOLLIN+EPOLLRDHUP+EPOLLHUP+EPOLLERR事件。
  • 对端shutdown写,本端会触发EPOLLIN+EPOLLRDHUP事件,如果本端继续在套接字上发送数据,则可以正常发送,对端也可以正常接收(即对端触发EPOLLIN信号)。
  • 对端shutdown读,对端自身会触发EPOLLIN+EPOLLRDHUP事件,但本端不会触发任何事件。如果对端已shutdown读,当本端继续在这个套接字上发送数据时,数据可以正常发送出去,对端则会触发EPOLLIN+EPOLLRDHUP事件,当然,如果对端愿意,仍然可以读取到这些数据。
  • 对端不管是close套接字,还是shutdown写,本端触发的都是EPOLLIN+EPOLLRDHUP事件,因此,本端无从区分对端是close了套接字,还是shutdown了写。但有一点可以区分,如果对端是close了套接字,则本端在套接字上发送数据时,本端会收到对端发来的RST报文从而本端会触发EPOLLIN+EPOLLRDHUP+EPOLLHUP+EPOLLERR事件;而如果对端只是shutdown了写,则本端可以正常发送数据不会触发任何信号。
  • 在边沿触发模式下,当套接字上有数据到达时,将触发套接字上的EPOLLIN(即可读事件)1次,如果此时不去读该套接字上的数据并且套接字上没有新的报文到达,则后续不会再触发EPOLLIN事件。相反,在水平模式下,如果不去读,则该套接字上会不断触发EPOLLIN事件)。但是,如果套接字上再次有数据到达时,不管之前的数据有没有去读取,都会再次触发EPOLLIN事件。也就是说,在边沿触发模式下,每次有新数据到达时都会触发EPOLLIN事件。
  • 在边沿触发模式下,如果对端关闭套接字(或shutdown写),则不管之前套接字上的数据是否读取完毕,都会触发该套接字上的EPOLLIN+EPOLLRDHUP事件。

综上,可能出现的事件组合为:
1. EPOLLIN:
可读
2. EPOLLOUT:
可写
3. EPOLLIN + EPOLLRDHUP:
只可能是两种情况:
一是收到对端发来的FIN报文(可能是对端close套接字或shutdown写);
二是本端showdown读。
注意,这两个信号会一直留存在epoll中,即在水平触发模式下,会不断触发EPOLLIN+EPOLLRDHUP事件;边沿触发模式下,如果该套接字上后续再有其他事件触发,这两个事件也会一并触发。
4. EPOLLIN+EPOLLHUP+EPOLLRDHUP:本端shutdown读写。为避免套接字上触发该种组合的事件,在shutdown读写前,应该将套接字从epoll中移除。
5. EPOLLIN+EPOLLRDHUP+EPOLLHUP+EPOLLERR
收到对端发来的RST报文。通常是对端已经close了套接字或shutdown了读写,本端还往对端发送数据,此时会收到对端发来的RST报文。

可以认为,EPOLLRDHUP事件不会单独出现,它总是和EPOLLIN一起出现。如果我们在shutdown读写前将套接字从epoll中移除,可以认为EPOLLERR 总是和EPOLLHUP一起出现,它俩不会单独出现。

你可能感兴趣的:(linux)