1. 程式人生 > >I/O多路複用之水平觸發和邊沿觸發模式

I/O多路複用之水平觸發和邊沿觸發模式

多路I/O複用不管是select,poll還是epoll,其都是通過同時監聽多個檔案描述符,當有檔案檔案描述符處於就緒狀態時,觸發通知。

LT(Level Trigger,水平觸發)模式和ET(Edge Trigger,邊沿觸發)模式是兩種檔案描述符準備就緒的通知模式。

水平觸發通知:如果檔案描述符上可以非阻塞地執行I/O系統呼叫,此時認為它已經就緒,觸發通知。
邊沿觸發通知:如果檔案描述符自上次狀態檢查以來有了新的I/O活動(比如新的輸入),此時需要觸發通知。

(1)當採用水平觸發通知時,我們可以在任意時刻檢查檔案描述符的就緒狀態。這表示當我們確定了檔案描述符處於就緒狀態時(比如存在有輸入資料),就可以對其執行一些I/O操作,然後重複檢查檔案描述符,看看是否仍然處於就緒態(比如還有更多的輸入資料),此時我們就能執行更多的I/O。舉個例子,比如說我們採用epoll水平觸發模式監聽一個檔案描述符的可讀,當這個檔案可讀就緒時,epoll會觸發一個通知,然後我們執行一次讀取操作,但這次操作我們並沒有把該檔案描述符的資料全部讀取完。當下一次呼叫epoll監聽該檔案描述符時,epoll還會再次觸發通知,直到該事件被處理完。這就意味著,當epoll觸發通知後,我們可以不立即處理該事件,當下次呼叫epoll監聽時,然後會再次嚮應用程式通告此事件,此時我們再處理也不晚。


(2)與之相反的是,當我們採用邊沿觸發時,只有當I/O事件發生時我們才會收到通知。還是上個例子,如果這次我們採用epoll的邊沿觸發模式監聽一個檔案描述符的可讀,當可讀就緒時,epoll會觸發一個通知,如果我們此時不立即處理該事件,當下次再呼叫epoll監聽時,雖然該檔案描述符的狀態是可讀的,但是此時epoll並不會再給應用程式傳送通知。因為在邊沿觸發工作模式下,只有下一個新的I/O事件到來時,才會再次傳送通知。另外,當檔案描述符收到I/O事件通知時,通常我們並不知道要處理多少I/O(例如有多少位元組可讀)。因此,採用邊沿觸發通知程式通常要按照如下規則來設計。

  • 在接收到一個I/O事件通知後,程式在某個時刻應該在相應的檔案描述符上儘可能多地執行I/O(比如儘可能多地讀取位元組)。如果程式沒這麼做,那麼就可能失去執行I/O的機會。因為直到產生另一個I/O事件為止,在此之前程式都不會再接收到通知了,因此也就不知道此時應該執行I/O操作。
  • 如果程式採用迴圈來對檔案描述符執行儘可能多的I/O,而檔案描述符又被設定為可阻塞的,那麼最終當沒有更多的I/O可執行時,I/O系統呼叫就會阻塞。基於這個原因,每個被檢查的檔案描述符通常應該置為非阻塞模式,在得到I/O事件通知後重復執行I/O操作,直到相應的系統呼叫(比如read(),write())以錯誤碼EAGAIN或EWOULDBLOCK的形式失敗。

可見,ET模式在很大程度上降低了同一個epoll事件被重複觸發的次數,因此效率要比LT模式高。

下表是I/O多路複用select,poll和epoll所支援的通知模型:

I/O模式 水平觸發 邊沿觸發
select() 支援 不支援
poll() 支援 不支援
epoll() 支援 支援

select和poll只支援LT工作模式,epoll的預設的工作模式是LT模式。當往epoll核心事件表中註冊一個檔案描述符上的EPOLLET事件時,epoll將以ET模式來操作該檔案描述符。關於epoll如何使用ET模式,我們在這裡就不再詳細介紹了。我會在後面介紹epoll的博文中詳細介紹epoll的LT模式和ET模式的使用。