天天看點

通俗易懂描述 epoll中的邊沿觸發和水準觸發

目錄

  • ​​epoll中兩種觸發模式介紹​​
  • 水準觸發
  • 邊沿觸發
  • ​​為什麼一定要把邊沿觸發和非阻塞io一起使用呢​​
  • ​​解決方法​​
  • ​​抛磚引玉​​
  • ​​歡迎大家批評指正,共同進步​​

epoll中兩種觸發模式介紹

水準觸發,就是指在條件滿足時,epoll_wait函數會一直來通知你,那麼什麼是這個條件呢? 這個條件就是指,假如我們在epoll_wait的第一個參數,也就是紅黑樹的樹根中,加入的都是讀事件,比如下面這樣的

event.events = EPOLLIN | EPOLLET;
event.data.fd = server_socket;
epoll_ctl(efd, EPOLL_CTL_ADD, server_socket, &event)
      

那麼我們第一次沒有将緩沖區中的資料全部讀出,那麼epoll_wait函數還會繼續通知我們,上面提到的條件可以了解成 緩沖區還有東西可以讀

就像高低電平一樣,隻有從高電平到低電平或者低電平到高電平時才會通知我們,還是上面這個例子,假如我們沒有把資料讀完,那麼epoll_wait函數便不再會通知我們,因為他已經通知過了,是否把緩沖區的資料讀完和epoll沒關系,這時隻有用戶端再次向伺服器端發送資料,epoll_wait才會傳回

為什麼一定要把邊沿觸發和非阻塞io一起使用呢

如果這裡使用阻塞IO,那麼也就是說,假如我們一次從緩沖區中讀取10個位元組,但是用戶端隻發了五個過來,那麼我們的IO就會阻塞住,因為緩沖區的東西不夠讀,這時候其他用戶端再次發送消息,epoll_wait也不會傳回,因為上一次epoll_wait傳回之後,代碼卡在了讀取這裡,這樣伺服器就無法處理其他資料了。

但是如果使用非阻塞IO,緩沖區的資料不夠讀了,那就不讀了,讀寫函數馬上傳回,不再等待,這樣就解決了上面的問題

解決方法

參考man文檔,輸入man epoll

Level-triggered and edge-triggered

The epoll event distribution interface is able to behave both as edge-triggered (ET) and as level-triggered (LT). The difference between the

two mechanisms can be described as follows. Suppose that this scenario happens:

1. The file descriptor that represents the read side of a pipe (rfd) is registered on the epoll instance.

   2. A pipe writer writes 2 kB of data on the write side of the pipe.

   3. A call to epoll_wait(2) is done that will return rfd as a ready file descriptor.

   4. The pipe reader reads 1 kB of data from rfd.

   5. A call to epoll_wait(2) is done.

   If  the  rfd  file descriptor has been added to the epoll interface using the EPOLLET (edge-triggered) flag, the call to epoll_wait(2) done in
   step 5 will probably hang despite the available data still present in the file input buffer; meanwhile the remote peer might  be  expecting  a
   response  based  on  the data it already sent.  The reason for this is that edge-triggered mode delivers events only when changes occur on the
   monitored file descriptor.  So, in step 5 the caller might end up waiting for some data that is already present inside the input  buffer.   In
   the  above example, an event on rfd will be generated because of the write done in 2 and the event is consumed in 3.  Since the read operation
   done in 4 does not consume the whole buffer data, the call to epoll_wait(2) done in step 5 might block indefinitely.

   An application that employs the EPOLLET flag should use nonblocking file descriptors to avoid having a blocking read or write  starve  a  task
   that is handling multiple file descriptors.  The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:

          i   with nonblocking file descriptors; and

          ii  by waiting for an event only after read(2) or write(2) return EAGAIN.(這裡的EAGAIN表示緩沖區沒有資料可讀,或者寫緩沖區已經寫滿了)
      

抛磚引玉

關于阻塞和非阻塞IO

歡迎大家批評指正,共同進步

繼續閱讀