IO多路複用的兩種模式:水平觸發和邊緣觸發

在 linux 的 IO 多路複用中有水平觸發,邊緣觸發兩種模式

  • 水平觸發 (level-trggered)
    只要文件描述符關聯的讀內核緩衝區非空,有數據可以讀取,就一直髮出可讀信號進行通知,
    當文件描述符關聯的內核寫緩衝區不滿,有空間可以寫入,就一直髮出可寫信號進行通知
    LT 模式支持阻塞和非阻塞兩種方式。epoll 默認的模式是 LT。

  • 邊緣觸發 (edge-triggered)
    當文件描述符關聯的讀內核緩衝區由空轉化爲非空的時候,則發出可讀信號進行通知,
    當文件描述符關聯的內核寫緩衝區由滿轉化爲不滿的時候,則發出可寫信號進行通知

兩者的區別在哪裏呢?

水平觸發是隻要讀緩衝區有數據,就會一直觸發可讀信號,而邊緣觸發僅僅在空變爲非空的時候通知一次,LT (level triggered) 是缺省的工作方式,並且同時支持 block 和 no-block socket. 在這種做法中,內核告訴你一個文件描述符是否就緒了,然後你可以對這個就緒的 fd 進行 IO 操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的 select/poll 都是這種模型的代表.

  • 水平觸發:如果有文件描述符已經就緒可以非阻塞的執行 IO 操作了,此時會觸發通知。允許在任意時刻重複檢測 IO 的狀態,沒有必要等待每次描述符就緒後才檢測,目的是儘可能多的執行 IO。

  • 邊緣觸發:如果文件描述符自上次狀態改變後有新的 IO 活動到來,此時會觸發通知。在收到一個 IO 事件通知後要儘可能多的執行 IO 操作,因爲如果在一次通知中沒有執行完 IO 那麼就需要等到下一次新的 IO 活動到來才能獲取到就緒的描述符。信號驅動式 IO 就屬於邊緣觸發.

  • select,poll 就屬於水平觸發。epoll 既可以採用水平觸發,也可以採用邊緣觸發。

舉個栗子

  • 第一個栗子:一個管道收到了 1kb 的數據,這時如果是水平觸發的,epoll 會立即返回,因爲有數據準備好了。如果是邊緣觸發的不會立即返回,因爲此時雖然有數據可讀但是已經觸發了一次通知,在這次通知到現在還沒有新的數據到來,直到有新的數據到來 epoll 纔會返回,此時老的數據和新的數據都可以讀取到 (當然是需要這次你儘可能的多讀取).

  • 在舉個讀 socket 的例子:現在來了 100 個字節,這時無論邊緣觸發和水平觸發都會產生一個 read ready notification 通知應用程序可讀。應用程序讀了 50 個字節,然後重新調用 api 等待 io 事件。這時水平觸發的 api 會因爲還有 50 個字節可讀從 而立即返回用戶一個 read ready notification。而邊緣觸發的 api 會因爲可讀這個狀態沒有發生變化而陷入長期等待。 因此在使用邊緣觸發的 api 時,要注意每次都要讀到 socket 返回 EWOULDBLOCK 爲止,否則這個 socket 就算廢了。而使用條件觸發的 api 時,如果應用程序不需要寫就不要關注 socket 可寫的事件,否則就會無限次的立即返回一個 write ready notification。

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