網路模型(3) 多路復用模型

2021-05-24 07:15:17 字數 3420 閱讀 3545

文章出處:http://www.cppblog.com/cppexplore/archive/2008/04/30/48529.html

多路復用的方式是真正實用的伺服器程式,非多路復用的網路程式只能作為學習或著陪測的角色

一、 select模型

select原型:

其中引數n表示監控的所有fd中最大值+1。

和select模型緊密結合的四個巨集,含義不解釋了:

理解select模型的關鍵在於理解fd_set,為說明方便,取fd_set長度為1位元組,fd_set中的每一bit可以對應乙個檔案描述符fd。則1位元組長的fd_set最大可以對應8個fd。

(1)執行fd_set set; fd_zero(&set);則set用位表示是0000,0000。

(2)若fd=5,執行fd_set(fd,&set);後set變為0001,0000(第5位置為1)

(3)若再加入fd=2,fd=1,則set變為0001,0011

(4)執行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為0000,0011。注意:沒有事件發生的fd=5被清空

基於上面的討論,可以輕鬆得出select模型的特點:

(1)可監控的檔案描述符個數取決與sizeof(fd_set)的值。我這邊伺服器上sizeof(fd_set)=512,每bit表示乙個檔案描述符,則我伺服器上支援的最大檔案描述符是512*8=4096。據說可調,另有說雖然可調,但調整上限受於編譯核心時的變數值。本人對調整fd_set的大小不太感興趣,參考http://www.cppblog.com/cppexplore/archive/2008/03/21/45061.html

中的模型2(1)可以有效突破select可監控的檔案描述符上限。

(2)將fd加入select監控集的同時,還要再使用乙個資料結構array儲存放到select監控集中的fd,一是用於在select返回後,array作為源資料和fd_set進行fd_isset判斷。二是select返回後會把以前加入的但並無事件發生的fd清空,則每次開始select前都要重新從array取得fd逐一加入(fd_zero最先),掃瞄array的同時取得fd最大值maxfd,用於select的第乙個引數

(3) 可見select模型必須在select前迴圈array(加fd,取maxfd),select返回後迴圈array(fd_isset判斷是否有時間發生)。

下面給乙個偽碼說明基本select模型的伺服器模型:

二、poll模型

poll原型:

和select相比,兩大改進

(1)不再有fd個數的上限限制可以將引數ufds想象成棧低指標,nfds是棧中元素個數,該棧可以無限制增長

(2) 引入pollfd結構,將fd資訊、需要監控的事件、返回的事件分開儲存,則poll返回後不會丟失fd資訊和需要監控的事件資訊,也就省略了select模型中前面的迴圈操作,返回後的迴圈仍然不可避免另每次poll阻塞操作都會自動把上次的revents清空

poll的伺服器模型偽碼:

注意select和poll中res的檢測,可有效減少迴圈的次數,這也是大量死連線存在時,select和poll效能下降厲害的原因

三、 epoll模型

epoll阻塞操作的原型:

與以上模型的優點:

(1) 它保留了poll的兩個相對與select的優點

(2) epoll_wait的引數events作為出參,直接返回了有事件發生的fd,epoll_wait的返回值既是發生事件的個數,省略了poll中返回之後的迴圈操作

(3)不再象select、poll一樣將識別符號侷限於fd,epoll中可以將識別符號擴大為指標,大大增加了epoll模型下的靈活性

epoll的伺服器模型偽碼:

epoll使用中的問題:

(1) epoll_ctl的epoll_ctl_del操作中,最後乙個引數是無意義的,但是在小版本號過低的2.6核心下要求最後乙個引數一定非null,否則返回失敗,並且返回的errno在man epoll_ctl中不存在,因此安全期間,保證epoll_ctl的最後乙個引數總非nulll。

(2) 如果乙個fd(比如管道)的事件導致了另乙個fd2的刪除,則必須掃瞄返回結果集中是否有fd2,有則在結果集中刪除,避免衝突。

(3) 有文章說epoll在g網環境下效能會低於poll/select,看有些測試,給出的拐點在2w/s併發之後,我本人的工作範圍不可能達到這麼高的併發,個人在測試效能的時候最大也是取的1w/s的併發,乙個是因為系統單程序允許開啟的檔案描述符最大值,4w的數字太高了,另乙個就是我這邊伺服器的效能達不到那麼高的效能,極限1.7w/s的響應,那測試的資料竟然在2w併發的時候還有2w的響應,不知道是什麼硬體配置。或許等有了g網的環境,會關注epoll高併發下的效能下降。

(4) epoll的lt和et效能的差異,我測試的資料表明兩者效能相當,「使用epoll就是為了高效能,就是要使用et模式」這個說法是站不住腳的。個人傾向於使用lt模式,程式設計簡單、安全。

四、 port模型

port則和epoll非常接近,不需要前後的兩次掃瞄,直接返回有事件的結果,可以象epoll一樣繫結指標,不同點是

(1) epoll可以返回多個事件,而port一次只返回乙個(port_getn可以返回多個,但是在不到指定的n值時,等待直到達到n個)

(2) port返回的結果會自動port_dissociate,如果要再次監控,需要重新port_associate

這個就不多說了。

可以看出select-->poll-->epoll/port的演化路線:

(1) 從readset、writeset等分離到 將讀寫事件集中到統一的結構

(2) 從阻塞操作前後的兩次迴圈 到 之後的一次迴圈  到精確返回有事件發生的fd

(3) 從只能繫結fd資訊,到可以繫結指標結構資訊

五、 抽象介面

綜合以上多路復用函式的特點,可以進行統一的封裝,這裡給出我封裝的介面,也算是給乙個思路:

使用的時候就是先init,再wait,再迴圈執行next_result直到空,每個result,使用get_data和get_event挨個處理,如果某個fd引起另乙個fd關閉,調delete_from_results(除epoll,其它都直接return),處理完reset_data(select和port用,poll/epoll直接return)。

IO模型 多路復用

乙個輸入操作通常包括兩個階段 應用程序被阻塞,直到資料從核心緩衝區複製到應用程序緩衝區中才返回。應該注意到,在阻塞的過程中,其它應用程序還可以執行,因此阻塞不意味著整個作業系統都被阻塞。因為其它應用程序還可以執行,所以不消耗 cpu 時間,這種模型的 cpu 利用率會比較高。應用程序執行系統呼叫之後...

IO多路復用 select模型

客戶端 見 c s通訊 伺服器阻塞型使用 伺服器端 include include include include include include include include include include include include int main set local address m...

多路復用模型之epoll

作為多路復用io模型,epoll致力於解決select與poll設計缺陷以提公升系統併發能力。1 併發效率不隨文句柄數上公升而線性下降 epoll避免了select模型中對所有每次所有檔案描述符控制代碼輪詢。它的底層採用紅黑樹記錄所有檔案控制代碼,並將活躍的連線存放至鍊錶中,在處理io事件時,只需遍...