多路複用之select,poll,epoll

一、什麼是多路複用

這個還是很好說清楚的。

假設你是一個機場的空管, 你需要管理到你機場的所有的航線, 包括進港,出港, 有些航班需要放到停機坪等待,有些航班需要去登機口接乘客。

你會怎麼做?

最簡單的做法,就是你去招一大批空管員,然後每人盯一架飛機, 從進港,接客,排位,出港,航線監控,直至交接給下一個空港,全程監控。

那麼問題就來了:

  • 很快你就發現空管塔裏面聚集起來一大票的空管員,交通稍微繁忙一點,新的空管員就已經擠不進來了。
  • 空管員之間需要協調,屋子裏面就1, 2個人的時候還好,幾十號人以後 ,基本上就成菜市場了。
  • 空管員經常需要更新一些公用的東西,比如起飛顯示屏,比如下一個小時後的出港排期,最後你會很驚奇的發現,每個人的時間最後都花在了搶這些資源上。

現實上我們的空管同時管幾十架飛機稀鬆平常的事情, 他們怎麼做的呢?

他們用這個東西

這個東西叫flight progress strip。 每一個塊代表一個航班,不同的槽代表不同的狀態,然後一個空管員可以管理一組這樣的塊(一組航班),而他的工作,就是在航班信息有新的更新的時候,把對應的塊放到不同的槽子裏面。

這個東西現在還沒有淘汰哦,只是變成電子的了而已。。

是不是覺得一下子效率高了很多,一個空管塔裏可以調度的航線可以是前一種方法的幾倍到幾十倍。

如果你把每一個航線當成一個Sock(I/O 流), 空管當成你的服務端Sock管理代碼的話。

  • 第一種方法就是最傳統的多進程併發模型 (每進來一個新的I/O流會分配一個新的進程管理。)
  • 第二種方法就是I/O多路複用 (單個線程,通過記錄跟蹤每個I/O流(sock)的狀態,來同時管理多個I/O流 。)

 

總結:

先講一下I/O模型:
首先,輸入操作一般包含兩個步驟:

其次瞭解一下常用的3種I/O模型:

1、阻塞I/O模型

最廣泛的模型是阻塞I/O模型,默認情況下,所有套接口都是阻塞的。 進程調用recvfrom系統調用,整個過程是阻塞的,直到數據複製到進程緩衝區時才返回(當然,系統調用被中斷也會返回)。

2、非阻塞I/O模型

當我們把一個套接口設置爲非阻塞時,就是在告訴內核,當請求的I/O操作無法完成時,不要將進程睡眠,而是返回一個錯誤。當數據沒有準備好時,內核立即返回EWOULDBLOCK錯誤,第四次調用系統調用時,數據已經存在,這時將數據複製到進程緩衝區中。這其中有一個操作時輪詢(polling)。

3、I/O複用模型

此模型用到select和poll函數,這兩個函數也會使進程阻塞,select先阻塞,有活動套接字才返回,但是和阻塞I/O不同的是,這兩個函數可以同時阻塞多個I/O操作,而且可以同時對多個讀操作,多個寫操作的I/O函數進行檢測,直到有數據可讀或可寫(就是監聽多個socket)。select被調用後,進程會被阻塞,內核監視所有select負責的socket,當有任何一個socket的數據準備好了,select就會返回套接字可讀,我們就可以調用recvfrom處理數據。
正因爲阻塞I/O只能阻塞一個I/O操作,而I/O複用模型能夠阻塞多個I/O操作,所以才叫做多路複用。

參考:https://www.cnblogs.com/52php/p/5684594.html

 

二、select, poll, epoll 都是I/O多路複用的具體的實現

具體實現參考:

https://blog.csdn.net/gatieme/article/details/50979090

三、總結

1、select有四個缺點:

1)最大併發數限制,因爲一個進程所打開的FD(文件描述符)是有限制的,由FD_SETSIZE設置(可以查看深入解析爲何select最多隻能監聽1024個),默認值是1024/2048,因此Select模型的最大併發數就被相應限制了。用戶可以自己修改FD_SETSIZE,然後重新編譯,但是其實,並不推薦這麼做
2)效率問題:select每次調用都會線性掃描全部的FD集合,這樣效率就會呈現線性下降

3)內核/用戶空間 內存拷貝問題,如何讓內核把FD消息通知給用戶空間呢?在這個問題上select採取了內存拷貝方法。

4)select 不是線程安全的

2、poll只解解決了select最大併發數的問題

3、epoll

1)沒有最大併發數限制

2)每個FD都會分配一個回調函數,如果FD準備就緒,觸發回調函數加入一個就緒隊列,epoll只對就緒隊列掃描。

3)使用共享內存

4)線程安全

select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,並喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒着”的時候要遍歷整個fd集合,而epoll在“醒着”的時候只要判斷一下就緒鏈表是否爲空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。

select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,並且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這裏的等待隊列並不是設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省不少的開銷。
 

 

最清楚的講解:

https://blog.csdn.net/u011671986/article/details/79449853

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