• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            CppExplore

            一切像霧像雨又像風(fēng)

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              29 隨筆 :: 0 文章 :: 280 評論 :: 0 Trackbacks

            作者:CppExplore 網(wǎng)址:http://www.shnenglu.com/CppExplore/
            多路復(fù)用的方式是真正實用的服務(wù)器程序,非多路復(fù)用的網(wǎng)絡(luò)程序只能作為學(xué)習(xí)或著陪測的角色。本文說下個人接觸過的多路復(fù)用函數(shù):select/poll/epoll/port。kqueue的*nix系統(tǒng)沒接觸過,估計熟悉了上面四種,kqueue也只是需要熟悉一下而已。
            一、select模型
            select原型:

            int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

            其中參數(shù)n表示監(jiān)控的所有fd中最大值+1。
            和select模型緊密結(jié)合的四個宏,含義不解釋了:

            FD_CLR(int fd, fd_set *set);
            FD_ISSET(
            int fd, fd_set *set);
            FD_SET(
            int fd, fd_set *set);
            FD_ZERO(fd_set 
            *set);

            理解select模型的關(guān)鍵在于理解fd_set,為說明方便,取fd_set長度為1字節(jié),fd_set中的每一bit可以對應(yīng)一個文件描述符fd。則1字節(jié)長的fd_set最大可以對應(yīng)8個fd。
            (1)執(zhí)行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。
            (2)若fd=5,執(zhí)行FD_SET(fd,&set);后set變?yōu)?001,0000(第5位置為1)
            (3)若再加入fd=2,fd=1,則set變?yōu)?001,0011
            (4)執(zhí)行select(6,&set,0,0,0)阻塞等待
            (5)若fd=1,fd=2上都發(fā)生可讀事件,則select返回,此時set變?yōu)?000,0011。注意:沒有事件發(fā)生的fd=5被清空。

            基于上面的討論,可以輕松得出select模型的特點:
            (1)可監(jiān)控的文件描述符個數(shù)取決與sizeof(fd_set)的值。我這邊服務(wù)器上sizeof(fd_set)=512,每bit表示一個文件描述符,則我服務(wù)器上支持的最大文件描述符是512*8=4096。據(jù)說可調(diào),另有說雖然可調(diào),但調(diào)整上限受于編譯內(nèi)核時的變量值。本人對調(diào)整fd_set的大小不太感興趣,參考http://www.shnenglu.com/CppExplore/archive/2008/03/21/45061.html中的模型2(1)可以有效突破select可監(jiān)控的文件描述符上限。
            (2)將fd加入select監(jiān)控集的同時,還要再使用一個數(shù)據(jù)結(jié)構(gòu)array保存放到select監(jiān)控集中的fd,一是用于再select返回后,array作為源數(shù)據(jù)和fd_set進(jìn)行FD_ISSET判斷。二是select返回后會把以前加入的但并無事件發(fā)生的fd清空,則每次開始select前都要重新從array取得fd逐一加入(FD_ZERO最先),掃描array的同時取得fd最大值maxfd,用于select的第一個參數(shù)。
            (3)可見select模型必須在select前循環(huán)array(加fd,取maxfd),select返回后循環(huán)array(FD_ISSET判斷是否有時間發(fā)生)。

            下面給一個偽碼說明基本select模型的服務(wù)器模型:

            array[slect_len];
            nSock
            =0;
            array[nSock
            ++]=listen_fd;(之前l(fā)isten port已綁定并listen)
            maxfd
            =listen_fd;
            while{
               FD_ZERO(
            &set);
               foreach (fd in array) 
               
            {
                   fd大于maxfd,則maxfd
            =fd
                   FD_SET(fd,
            &set)
               }

               res=select(maxfd
            +1,&set,0,0,0);
               
            if(FD_ISSET(listen_fd,&set))
               
            {
                   newfd
            =accept(listen_fd);
                   array[nsock
            ++]=newfd;
                        if(--res<=0) continue
               }

               foreach 下標(biāo)1開始 (fd in array) 
               
            {
                   
            if(FD_ISSET(fd,&set))
                      執(zhí)行讀等相關(guān)操作
                      如果錯誤或者關(guān)閉,則要刪除該fd,將array中相應(yīng)位置和最后一個元素互換就好,nsock減一
                         if(--res<=0) continue

               }

            }

            二、poll模型
            poll原型:

            int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
            struct pollfd 
            {
                                   
            int fd;           /* file descriptor */
                                   
            short events;     /* requested events */
                                   
            short revents;    /* returned events */
                           }
            ;

            和select相比,兩大改進(jìn):
            (1)不再有fd個數(shù)的上限限制,可以將參數(shù)ufds想象成棧低指針,nfds是棧中元素個數(shù),該棧可以無限制增長
            (2)引入pollfd結(jié)構(gòu),將fd信息、需要監(jiān)控的事件、返回的事件分開保存,則poll返回后不會丟失fd信息和需要監(jiān)控的事件信息,也就省略了select模型中前面的循環(huán)操作,返回后的循環(huán)仍然不可避免。另每次poll阻塞操作都會自動把上次的revents清空。
            poll的服務(wù)器模型偽碼:

            struct pollfd fds[POLL_LEN];
            unsigned 
            int nfds=0;
            fds[
            0].fd=server_sockfd;
            fds[
            0].events=POLLIN|POLLPRI;
            nfds
            ++;
            while{
              res=poll(fds,nfds,
            -1);
              
            if(fds[0].revents&(POLLIN|POLLPRI)){執(zhí)行accept并加入fds中,if(--res<=0)continue}
              循環(huán)之后的fds,
            if(fds[i].revents&(POLLIN|POLLERR )){操作略if(--res<=0)continue}
            }
            注意select和poll中res的檢測,可有效減少循環(huán)的次數(shù),這也是大量死連接存在時,select和poll性能下降厲害的原因。

            三、epoll模型

            epoll阻塞操作的原型:

            int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
            epoll引入了新的結(jié)構(gòu)epoll_event。
            typedef union epoll_data 
            {
                             
            void *ptr;
                             
            int fd;
                             __uint32_t u32;
                             __uint64_t u64;
                        }
             epoll_data_t;

                        struct epoll_event 
            {
                             __uint32_t events;  
            /* Epoll events */
                             epoll_data_t data;  
            /* User data variable */
                        }
            ;

            與以上模型的優(yōu)點:
            (1)它保留了poll的兩個相對與select的優(yōu)點
            (2)epoll_wait的參數(shù)events作為出參,直接返回了有事件發(fā)生的fd,epoll_wait的返回值既是發(fā)生事件的個數(shù),省略了poll中返回之后的循環(huán)操作。
            (3)不再象select、poll一樣將標(biāo)識符局限于fd,epoll中可以將標(biāo)識符擴大為指針,大大增加了epoll模型下的靈活性。
            epoll的服務(wù)器模型偽碼:

            epollfd=epoll_create(EPOLL_LEN);
            epoll_ctl(epollfd,EPOLL_CTL_ADD,server_sockfd,
            &ev)
            struct epoll_event events[EPOLL_MAX_EVENT];
            while
            {
            nfds
            =epoll_wait(epollfd,events,EPOLL_MAX_EVENT,-1);
            循環(huán)nfds,是server_sockfd則accept,否則執(zhí)行響應(yīng)操作
            }

            epoll使用中的問題:
            (1)epoll_ctl的EPOLL_CTL_DEL操作中,最后一個參數(shù)是無意義的,但是在小版本號過低的2.6內(nèi)核下要求最后一個參數(shù)一定非NULL,否則返回失敗,并且返回的errno在man epoll_ctl中不存在,因此安全期間,保證epoll_ctl的最后一個參數(shù)總非NULLL。
            (2)如果一個fd(比如管道)的事件導(dǎo)致了另一個fd2的刪除,則必須掃描返回結(jié)果集中是否有fd2,有則在結(jié)果集中刪除,避免沖突。
            (3)有文章說epoll在G網(wǎng)環(huán)境下性能會低于poll/select,看有些測試,給出的拐點在2w/s并發(fā)之后,我本人的工作范圍不可能達(dá)到這么高的并發(fā),個人在測試性能的時候最大也是取的1w/s的并發(fā),一個是因為系統(tǒng)單進(jìn)程允許打開的文件描述符最大值,4w的數(shù)字太高了,另一個就是我這邊服務(wù)器的性能達(dá)不到那么高的性能,極限1.7w/s的響應(yīng),那測試的數(shù)據(jù)竟然在2w并發(fā)的時候還有2w的響應(yīng),不知道是什么硬件配置。或許等有了G網(wǎng)的環(huán)境,會關(guān)注epoll高并發(fā)下的性能下降


            (4)epoll的LT和ET性能的差異,我測試的數(shù)據(jù)表明兩者性能相當(dāng),“使用epoll就是為了高性能,就是要使用ET模式”這個說法是站不住腳的。個人傾向于使用LT模式,編程簡單、安全。

            四、port模型
            port則和epoll非常接近,不需要前后的兩次掃描,直接返回有事件的結(jié)果,可以象epoll一樣綁定指針,不同點是
            (1)epoll可以返回多個事件,而port一次只返回一個(port_getn可以返回多個,但是在不到指定的n值時,等待直到達(dá)到n個)
            (2)port返回的結(jié)果會自動port_dissociate,如果要再次監(jiān)控,需要重新port_associate
            這個就不多說了。

            可以看出select-->poll-->epoll/port的演化路線:
            (1)從readset、writeset等分離到 將讀寫事件集中到統(tǒng)一的結(jié)構(gòu)
            (2)從阻塞操作前后的兩次循環(huán) 到 之后的一次循環(huán)  到精確返回有事件發(fā)生的fd
            (3)從只能綁定fd信息,到可以綁定指針結(jié)構(gòu)信息

            五、抽象接口
            綜合以上多路復(fù)用函數(shù)的特點,可以進(jìn)行統(tǒng)一的封裝,這里給出我封裝的接口,也算是給一個思路:

             virtual int init()=0;
             virtual 
            int wait()=0;
             virtual 
            void * next_result()=0;
             virtual 
            void delete_from_results(void * data)=0;
             virtual 
            void * get_data(void * event)=0;
             virtual 
            int get_event(void * event)=0;
             virtual 
            int add_data(int fd,XPollData * data)=0;
             virtual 
            int delete_data(int fd,XPollData *data)=0;
             virtual 
            int change_data(int fd,XPollData *data)=0;
             virtual 
            int reset_data(int fd,XPollData *data)=0;

            使用的時候就是先init,再wait,再循環(huán)執(zhí)行next_result直到空,每個result,使用get_data和get_event挨個處理,如果某個fd引起另一個fd關(guān)閉,調(diào)delete_from_results(除epoll,其它都直接return),處理完reset_data(select和port用,poll/epoll直接return)。

            posted on 2008-04-30 17:23 cppexplore 閱讀(8325) 評論(8)  編輯 收藏 引用

            評論

            # re: 【原創(chuàng)】系統(tǒng)設(shè)計之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型[未登錄] 2008-04-30 18:13 true
            關(guān)注epoll和線程池的結(jié)合。  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】系統(tǒng)設(shè)計之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型[未登錄] 2008-04-30 18:47 CppExplore
            @true
            可以在上一篇《網(wǎng)絡(luò)模型二》http://www.shnenglu.com/CppExplore/archive/2008/03/21/45061.html中跟貼討論新模型,是文中已有的,還是沒有的新模型,本文旨在討論函數(shù)本身的差異。
            epoll和線程池的結(jié)合,不是很理解,是epoll單線程后接業(yè)務(wù)線程,還是epoll本身線程池?epoll本身的話,我想象不出來,結(jié)構(gòu)在可讀狀態(tài)的話,新線程epoll_wait還是可讀。并且線程池要求無內(nèi)蘊狀態(tài),行為的變化來自于外界的輸入?yún)?shù),莫非是《網(wǎng)絡(luò)模型二》中的模型2(1)?
            就epoll而言,《網(wǎng)絡(luò)模型二》的2模型中,業(yè)務(wù)線程先不談,單線程的epoll性能就很強,其他模型的測試結(jié)果反而有下降。poll/select則不同。
            這個話題最好還是在《網(wǎng)絡(luò)模型二》中探討,本文不多擴展。  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】系統(tǒng)設(shè)計之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型[未登錄] 2008-04-30 20:07 true
            在《網(wǎng)絡(luò)模型二》中回復(fù)了,請討論  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】技術(shù)系列之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型 2008-09-03 12:23 bluesky
            (4)epoll的LT和ET性能的差異,我測試的數(shù)據(jù)表明兩者性能相當(dāng),“使用epoll就是為了高性能,就是要使用ET模式”這個說法是站不住腳的。個人傾向于使用LT模式,編程簡單、安全。

            epoll LT與ET的性能差別還是蠻大的,LT單臺能撐到10wcpu已經(jīng)是極限,但ET撐到15w沒啥問題。不過如果應(yīng)用不要求撐到這么高的在線,那確實LT就可以滿足了。  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】技術(shù)系列之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型 2008-11-07 11:21 ddd
            轉(zhuǎn)載  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】技術(shù)系列之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型 2009-02-24 11:11 ff
            很高興看到博主的好文,謝謝~~~希望繼續(xù)寫下去  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】技術(shù)系列之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型 2009-06-05 18:09 上海蘇元電氣科技有限公司
            上海蘇元電氣科技有限公司,以國內(nèi)電氣領(lǐng)域服務(wù)專家為奮斗目標(biāo),自成立以來致力于通過技術(shù)創(chuàng)新和優(yōu)質(zhì)服務(wù),深化產(chǎn)品內(nèi)涵,與廣大用戶共贏,與合作伙伴共贏。通過實行營銷專業(yè)化,品種規(guī)格市場化,堅持自主研發(fā)的道路,形成了具有自主知識產(chǎn)權(quán) 的“SUWIN”品牌和先進(jìn)完整成熟的產(chǎn)品設(shè)計、制造、銷售技術(shù)體系。具備了獨立開發(fā)具有國際先進(jìn)水平的新產(chǎn)品,新技術(shù)能力。

            也是一種方式,公司剛弄起這些.  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】技術(shù)系列之 網(wǎng)絡(luò)模型(三)多路復(fù)用模型 2015-03-27 17:18 ckw
            文中一、select模型 使用select時也無法突破最大fd=1024的限制啊!  回復(fù)  更多評論
              


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久精品亚洲一区二区三区浴池| 久久精品男人影院| 2021国产精品久久精品| 久久国产精品无码网站| 久久久久97国产精华液好用吗| 国内精品久久久久影院一蜜桃| 国产亚洲色婷婷久久99精品| 欧美激情精品久久久久久久九九九| 一97日本道伊人久久综合影院| 无码精品久久一区二区三区| 久久久久国产| 久久国产精品波多野结衣AV | 久久精品国产亚洲精品2020| 久久这里都是精品| 久久精品成人免费网站| 久久久亚洲欧洲日产国码aⅴ| 精品久久人妻av中文字幕| 精品久久久久香蕉网| 国产美女久久精品香蕉69| 国产精品18久久久久久vr| 久久无码精品一区二区三区| 无码人妻少妇久久中文字幕| 狠狠综合久久综合88亚洲| 99久久免费只有精品国产| 精品久久久中文字幕人妻| 狠狠人妻久久久久久综合蜜桃| 色天使久久综合网天天| 久久香蕉国产线看观看精品yw| 一级女性全黄久久生活片免费| 久久精品国产男包| 高清免费久久午夜精品| 国产亚洲精午夜久久久久久| 国产精品久久久久蜜芽| 久久精品国产亚洲AV无码娇色| 久久综合视频网站| 日韩AV无码久久一区二区| 99久久精品国产一区二区| 影音先锋女人AV鲁色资源网久久| 狠狠久久综合伊人不卡| 亚洲精品无码久久久影院相关影片| 国内精品伊人久久久影院|