【作業系統】I/O多路復用 select poll epoll

@

I/O模式

  • 阻塞I/O
  • 非阻塞I/O
  • I/O多路復用
  • 訊號驅動I/O
  • 非同步I/O

I/O多路復用

I/O 多路復用 相較於多進程多執行緒技術區別在於一個進程或執行緒可以處理多個事件。I/O多路復用通過一種機制,可以監視多個描述符,一旦某個描述符就緒,能夠通知相應的進程/執行緒進行相應操作

select、poll、epoll都是IO多路復用的機制,它們可以同時監控多個fd的操作

文件描述符(fd):內核利用文件描述符來訪問文件。文件描述符是非負整數。打開文件或新建文件時,內核會返回一個文件描述符。

select

將多個fd放到一個文件描述符集合(數組),將其拷貝到內核 bitmap 中同時監視多個fd只要有任何一個數據狀態準備就緒了,就將其標記為可讀或可寫,再接著把整個文件描述符集合拷貝回用戶態,用戶態還需要通過遍歷找到可讀或可寫的fd,然後對其處理。

缺點

  • select這種方式需要進行2次 遍歷 文件描述符集合,一次在內核態里,一次在用戶態里。
  • 還會發生2次 拷貝 文件描述符集合,先從用戶空間傳到內核空間,由內核修改後,再傳出到用戶空間。
  • select使⽤固定⻓度的 BitsMap 表示文件描述符集合,個數是有限制的,最多為 1024 個。

poll

poll select 並沒有太大本質區別,只是不再用 BitsMap 來存儲所關注的⽂件描述符,而是用動態數組,以鏈表的形式來組織,不再有最大文件描述符數量的限制。

epoll

epoll 在內核中保存了一份文件描述符集合,底層數據結構是紅黑樹,增刪查時間複雜度都是O(logn),通過對這顆紅黑樹進行操作,就不需要像select/poll每次操作都從用戶態傳入整個文件描述符集合到內核態,減少了內核和用戶空間的數據拷貝和記憶體分配

內核不再通過輪詢的方式找到就緒的文件描述符,而是通過非同步 IO 事件喚醒,將其通過回調函數加入到一個就緒隊列中(雙向鏈表),當返回時只返回就緒隊列中有I/O事件的文件描述符,所以不需要像select/poll 那樣掃描整個集合,大大提高了檢測效率。

事件觸發模式

epoll 支援兩種事件觸發模式,分別是 邊緣觸發水平觸發

  • 邊緣觸發(ET)

使用邊緣觸發模式時,當監控的文件描述符有可讀寫事件發生時,伺服器只會從epoll_wait中蘇醒一次。

也就是說每有一個可讀寫事件發生,伺服器就只會蘇醒一次,就算一次沒有讀取完也不會再次蘇醒。

因此我們程式要保證⼀次性將內核緩衝區的數據讀取完;所以邊緣觸發模式一般和非阻塞I/O搭配使用

  • 水平觸發(LT)

使用水平觸發模式時,當監控的文件描述符有可讀寫事件發生時,epoll_wait會通知處理程式去讀寫,如果這次沒有把數據一次性全部讀寫完(如讀寫緩衝區太小),那麼會一直通知你告訴你,直到內核緩衝區讀取完

參考

徹底搞懂IO多路復用 (qq.com)