多路復用伺服器網路模型

2021-05-23 06:31:44 字數 3073 閱讀 6595

多路復用的方式是真正實用的伺服器程式,非多路復用的網路程式只能作為學習或著陪測的角色。本文說下個人接觸過的多路復用函式:select/poll/epoll/port。kqueue的*nix系統沒接觸過,估計熟悉了上面四種,kqueue也只是需要熟悉一下而已。

一、select模型

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模型的伺服器模型:

和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)。

多路復用 迴圈伺服器模型

include header.h typedef struct sockaddr sa define maxline 1024 int main void memset server addr,0,sizeof server addr server addr.sin family af inet s...

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

文章出處 http www.cppblog.com cppexplore archive 2008 04 30 48529.html 多路復用的方式是真正實用的伺服器程式,非多路復用的網路程式只能作為學習或著陪測的角色 一 select模型 select原型 其中引數n表示監控的所有fd中最大值 1...

IO多路復用併發伺服器

伺服器呼叫select函式檢測兩種不同型別的輸入事件 1 新的客戶端請求到達,此時監聽描述符準備好可以讀了 伺服器開啟連線並將該客戶新增到池裡 2 乙個已存在的客戶端的已連線描述符準備好可以讀了 伺服器把來自每個已經準備好的已連線描述符的乙個文字行回送回去 當且僅當乙個從該描述符讀取乙個位元組的請求...