LT水平触发模式
-
epoll_wait检测到文件描述符有事件发生,则将其通知给应用程序,应用程序可以不立即处理该事件。
-
当下一次调用epoll_wait时,epoll_wait还会再次向应用程序报告此事件,直至被处理。
ET边缘触发模式
-
epoll_wait检测到文件描述符有事件发生,则将其通知给应用程序,应用程序必须立即处理该事件
-
必须要一次性将数据读取完,使用非阻塞I/O,读取到出现eagain
- 为什么ET模式下需要使用非阻塞?
- 每次通过 read 系统调用读取数据时,最多只能读取缓冲区大小的字节数;如果某个文件描述符一次性收到的数据超过了缓冲区的大小,那么需要对其 read 多次才能全部读取完毕
- select 可以使用阻塞 I/O。通过 select 获取到所有可读的文件描述符后,遍历每个文件描述符,read 一次数据,这些文件描述符都是可读的,因此即使 read 是阻塞 I/O,也一定可以读到数据,不会一直阻塞下去。
- select 采用水平触发模式,因此如果第一次 read 没有读取完全部数据,那么下次调用 select 时依然会返回这个文件描述符,可以再次 read
- select 也可以使用非阻塞 I/O。当遍历某个可读文件描述符时,使用 for 循环调用 read 多次,直到读取完所有数据为止(返回 EWOULDBLOCK)。这样做会多一次 read 调用,但可以减少调用 select 的次数。
- 在 epoll 的边缘触发模式下,只会在文件描述符的可读/可写状态发生切换时,才会收到操作系统的通知,因此,如果使用 epoll 的边缘触发模式,在收到通知时,必须使用非阻塞 I/O,并且必须循环调用 read 或 write 多次,直到返回 EWOULDBLOCK 为止,然后再调用 epoll_wait 等待操作系统的下一次通知。
- 如果没有一次性读/写完所有数据,那么在操作系统看来这个文件描述符的状态没有发生改变,将不会再发起通知,调用 epoll_wait 会使得该文件描述符一直等待下去,服务端也会一直等待客户端的响应,业务流程无法走完。这样做的好处是每次调用 epoll_wait 都是有效的——保证数据全部读写完毕了,等待下次通知。在水平触发模式下,如果调用 epoll_wait 时数据没有读/写完毕,会直接返回,再次通知。因此边缘触发能显著减少事件被触发的次数。
- 为什么 epoll 的边缘触发模式不能使用阻塞 I/O?很显然,边缘触发模式需要循环读/写一个文件描述符的所有数据。如果使用阻塞 I/O,那么一定会在最后一次调用(没有数据可读/写)时阻塞,导致无法正常结束。
- 为什么ET模式下需要使用非阻塞?
EPOLLONESHOT
-
一个线程读取某个socket上的数据后开始处理数据,在处理过程中该socket上又有新数据可读,此时另一个线程被唤醒读取,此时出现两个线程处理同一个socket
-
我们期望的是一个socket连接在任一时刻都只被一个线程处理,通过epoll_ctl对该文件描述符注册epolloneshot事件,一个线程处理socket时,其他线程将无法处理,当该线程处理完后,需要通过epoll_ctl重置epolloneshot事件