目錄
- 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
歡迎大家批評指正,共同進步