[python異步 第一篇 ] Linux 多路複用IO模型 Select,Poll,Epoll 對比


操作系統在處理io的時候,主要有兩個階段:

  • 等待數據傳到io設備
  • io設備將數據複製到user space

我們一般將上述過程簡化理解爲:

  • 等到數據傳到kernel內核space
  • kernel內核區域將數據複製到user space(理解爲進程或者線程的緩衝區)

而根據這兩個階段而不同的操作方法,就會產生多種io模型,本文只討論select,poll,epoll,所以只引出三種io模型。

一、細談 io 多路複用技術 select 和poll

Select介紹

select的工作流程: 單個進程就可以同時處理多個網絡連接的io請求(同時阻塞多個io操作)。基本原理就是程序呼叫select,然後整個程序就阻塞了,這時候,kernel就會輪詢檢查所有select負責的fd,當找到一個client中的數據準備好了,select就會返回,這個時候程序就會系統調用,將數據從kernel複製到進程緩衝區。

io_select_process

下圖爲select同時從多個客戶端接受數據的過程

雖然服務器進程會被select阻塞,但是select會利用內核不斷輪詢監聽其他客戶端的io操作是否完成。
io_select_time

Poll介紹

poll的原理與select非常相似,差別如下:

  • 描述fd集合的方式不同,poll使用 pollfd 結構而不是select結構fd_set結構,所以poll是鏈式的,沒有最大連接數的限制
  • poll有一個特點是水平觸發,也就是通知程序fd就緒後,這次沒有被處理,那麼下次poll的時候會再次通知同個fd已經就緒。

Select、Poll 的缺點

  • 根據fd_size的定義,它的大小爲32個整數大小(32位機器爲32*32,所有共有1024bits可以記錄fd),每個fd一個bit,所以最大隻能同時處理1024個fd(select有這個限制,poll沒有)

  • 每次要判斷【有哪些event發生】這件事的成本很高,因爲select(polling也是)採取主動輪詢機制

Select、Poll 的輪詢過程

  1. 每一次呼叫 select( ) 都需要先從 user space把 FD_SET複製到 kernel(約線性時間成本) 爲什麼 select 不能像epoll一樣,只做一次複製就好呢? 每一次呼叫 select()前,FD_SET都可能更動,而 epoll 提供了共享記憶存儲結構,所以不需要有 kernel 與 user之間的數據溝通

  2. kernel還要輪詢每個fd,約線性時間

    • 假設現實中,有1百萬個客戶端同時與一個服務器保持着tcp連接,而每一個時刻,通常只有幾百上千個tcp連接是活躍的,這時候我們仍然使用select/poll機制,kernel必須在搜尋完100萬個fd之後,才能找到其中狀態是active的,這樣資源消耗大而且效率低下。

對於select和poll的上述缺點,就引進了一種新的技術,epoll技術

二、細談事件驅動–Epoll

Epoll提供了三個函數:

  1. int epoll_create(int size); 建立一個 epoll 對象,並傳回它的id
  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 事件註冊函數,將需要監聽的事件和需要監聽的fd交給epoll對象
  3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 等待註冊的事件被觸發或者timeout發生

Epoll解決的問題:

  • epoll沒有fd數量限制 epoll沒有這個限制,我們知道每個epoll監聽一個fd,所以最大數量與能打開的fd數量有關,一個g的內存的機器上,能打開10萬個左右
  • epoll不需要每次都從user space 將fd set複製到內核kernel epoll在用epoll_ctl函數進行事件註冊的時候,已經將fd複製到內核中,所以不需要每次都重新複製一次
  • select 和 poll 都是主動輪詢機制,需要拜訪每一個 FD; epoll是被動觸發方式,給fd註冊了相應事件的時候,我們爲每一個fd指定了一個回調函數,當數據準備好之後,就會把就緒的fd加入一個就緒的隊列中,epoll_wait的工作方式實際上就是在這個就緒隊列中查看有沒有就緒的fd,如果有,就喚醒就緒隊列上的等待者,然後調用回調函數。
  • 雖然epoll、poll、select都需要查看是否有fd就緒,但是epoll之所以是被動觸發,就在於它只要去查找就緒隊列中有沒有fd,就緒的fd是主動加到隊列中,epoll不需要一個個輪詢確認。 換一句話講,就是select和poll只能通知有fd已經就緒了,但不能知道究竟是哪個fd就緒,所以select和poll就要去主動輪詢一遍找到就緒的fd。而epoll則是不但可以知道有fd可以就緒,而且還具體可以知道就緒fd的編號,所以直接找到就可以,不用輪詢。

總結

  • select, poll是爲了解決同時大量IO的情況(尤其網絡服務器),但是隨着連接數越多,性能越差
  • epoll是select和poll的改進方案,在 linux 上可以取代 select 和 poll,可以處理大量連接的性能問題

推薦閱讀:https://blog.csdn.net/xiexievv/article/details/44976215

ll,可以處理大量連接的性能問題

推薦閱讀:https://blog.csdn.net/xiexievv/article/details/44976215

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