EPOLL在ET和LT下的讀寫

epoll是Linux上一個可擴展的I/O事件通知機制。主要有兩種工作模式

Level Triggered ( LT )水平觸發,默認方式,即當epoll_wait檢測到描述符事件發生並將此事件通知應用程序,應用程序可以不立即處理該事件。下次調用epoll_wait時,會再次響應應用程序並通知此事件。

Edge Triggered ( ET )邊緣觸發,即當epoll_wait檢測到描述符事件發生並將此事件通知應用程序,應用程序必須立即處理該事件。如果不處理,下次調用epoll_wait時,不會再次響應應用程序並通知此事件。

注:ET模式在很大程度上減少了epoll事件被重複觸發的次數,因此效率要比LT模式高。epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由於一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。

那麼對應到socket編程中的accept, read, write,有必要詳細說說。

在LT模式下,accept,read,write和平時的編程方式並沒有要特別注意的地方,因爲只要對應的文件描述符中還有數據未讀取或者處於可寫狀態,都會有通知。例如說,在read的時候沒有一次性把緩衝區的數據全部讀出來,那麼epoll還會再次通知此事件。可以在阻塞和非阻塞的套接字上使用

然而,在ET模式下,epoll只在事件發生時發生通知,沒有新的事件發生就不通知。例如,在read的時候沒有一次性把緩衝區的數據全部讀出來,那麼epoll不會再次通知此事件。除非有新的事件發生,不然監聽的描述符是無法被觸發。只能在非阻塞的套接字上使用

Edge Triggered event distribution delivers events only when events happens on the monitored file 。

ET模式下accept存在的問題
考慮這種情況:多個連接同時到達,服務器的TCP就緒隊列瞬間積累多個就緒連接,由於是邊緣觸發模式,epoll只會通知一次,accept只處理一個連接,導致TCP就緒隊列中剩下的連接都得不到處理。

解決辦法是用while循環抱住accept調用,處理完TCP就緒隊列中的所有連接後再退出循環。如何知道是否處理完就緒隊列中的所有連接呢?accept返回-1並且errno設置爲EAGAIN就表示所有連接都處理完。

while ((conn_sock = accept(listenfd,(struct sockaddr *) &remote, (size_t *)&addrlen)) > 0) {
     handle_client(conn_sock);
}
if (conn_sock == -1) {
        if (errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
    perror("accept");
}

ET模式下,讀取要一次把緩衝區的數據讀完

  • 如果read返回0,那麼說明已經接受所有數據
  • 如果errno=EAGAIN,說明還有數據未接收,等待下一次通知
  • 如果read返回-1,說明發生錯誤,停止處理

Receiving an event from epoll_wait(2) should suggest to you that such file descriptor is ready for the requested I/O operation. You have simply to consider it ready until you will receive the next EAGAIN. When and how you will use such file descriptor is entirely up to you. Also, the condition that the read/write I/O space is exhausted can be detected by checking the amount of data read/write from/to the target file descriptor. For example, if you call read(2) by asking to read a certain amount of data and read(2) returns a lower number of bytes, you can be sure to have exhausted the read I/O space for such file descriptor. Same is valid when writing using the write(2) function.

以上是官方文檔的解釋,一旦得到通知說明I/O已經準備好了,不管你什麼時候去處理,可以保證讀取完緩衝區的數據。寫也是同樣道理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章