浅谈epoll的LT和ET
怎么设置ET
在register event的时候,在event中传入EPOLLET
flag就行了。
LT和ET是啥
它们唯一的区别就是“通知方式”不同。
以readable为例:
LT:只要某fd在readable状态,就会一直提醒(指epoll_wait()马上返回)。
ET:只在某fd从unreadable状态变为readable状态时epoll_wait()才会返回。
来个具体的例子
https://en.wikipedia.org/wiki/Epoll
For instance, if a pipe registered with epoll
has received data, a call to epoll_wait
will return, signaling the presence of data to be read. Suppose, the reader only consumed part of data from the buffer. In level-triggered mode, further calls to epoll_wait
will return immediately, as long as the pipe’s buffer contains data to be read. In edge-triggered mode, however, epoll_wait
will return only once new data is written to the pipe.
但是为啥叫LT/ET?
有点数字电路内味了。
LT就是:只要fd的状态一直在readable的level,那就一直触发提醒。
ET就是:只有在fd的状态从 unreadable 到 readable 的那条edge上才会触发提醒。
使用ET的注意事项
https://man7.org/linux/man-pages/man7/epoll.7.html
The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:
a) with nonblocking file descriptors; and
b) by waiting for an event only after read(2) or write(2) return EAGAIN.
https://stackoverflow.com/questions/14643249/why-having-to-use-non-blocking-fd-in-a-edge-triggered-epoll-function
为什么ET要non-blocking呢?因为ET的话fd里面的残留不会通知,所以每次都要把fd读干净才行。如果blocking,那read很可能block住,然后把程序卡死。
所以最好的方式就是:把fd设成非阻塞,然后一直读,直到返回EAGAIN,就说明读干净了。
什么时候应该用ET
https://stackoverflow.com/questions/9162712/what-is-the-purpose-of-epolls-edge-triggered-option
简单来说就是:有时候我们不想马上处理一个read。所以LT别老在那烦我。
When an FD becomes read or write ready, you might not necessarily want to read (or write) all the data immediately.
Level-triggered epoll will keep nagging you as long as the FD remains ready, whereas edge-triggered won’t bother you again until the next time you get an
EAGAIN
(so it’s more complicated to code around, but can be more efficient depending on what you need to do).Say you’re writing from a resource to an FD. If you register your interest for that FD becoming write ready as level-triggered, you’ll get constant notification that the FD is still ready for writing. If the resource isn’t yet available, that’s a waste of a wake-up, because you can’t write any more anyway.
If you were to add it as edge-triggered instead, you’d get notification that the FD was write ready once, then when the other resource becomes ready you write as much as you can. Then if
write(2)
returnsEAGAIN
, you stop writing and wait for the next notification.The same applies for reading, because you might not want to pull all the data into user-space before you’re ready to do whatever you want to do with it (thus having to buffer it, etc etc). With edge-triggered epoll you get told when it’s ready to read, and then can remember that and do the actual reading “as and when”.
还有一个场景,就是多线程环境中,事件到来时只想唤醒一个线程,可以用ET。LT可能唤醒多个线程处理一个事件,拉了。
ET is also particularly nice with a multithreaded server on a multicore machine. You can run one thread per core and have all of them call epoll_wait on the same epfd. When data comes in on an fd, exactly one thread will be woken to handle it.