• <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>

            那誰(shuí)的技術(shù)博客

            感興趣領(lǐng)域:高性能服務(wù)器編程,存儲(chǔ),算法,Linux內(nèi)核
            隨筆 - 210, 文章 - 0, 評(píng)論 - 1183, 引用 - 0
            數(shù)據(jù)加載中……

            lighttpd1.4.18代碼分析(三)--網(wǎng)絡(luò)IO事件處理器的使用

            本節(jié)是第二節(jié)lighttpd1.4.18代碼分析(二)--fdevents結(jié)構(gòu)體解析的延續(xù),在閱讀本節(jié)內(nèi)容之前,請(qǐng)先閱讀上一節(jié)內(nèi)容.

            上一節(jié)已經(jīng)對(duì)lighttpd中的fdevent結(jié)構(gòu)體進(jìn)行了分析,前面提過(guò),fdevent結(jié)構(gòu)體是網(wǎng)絡(luò)IO事件處理器的"虛擬基類(lèi)",提供了網(wǎng)絡(luò)IO事件處理器的公共成員,私有成員以及對(duì)外接口,這一節(jié)將對(duì)這個(gè)事件處理器的實(shí)現(xiàn)和使用進(jìn)行解析.與這些相關(guān)的文件有這些:fdevent.h提供了fdevent結(jié)構(gòu)體的定義, 在這個(gè)頭文件中聲明的函數(shù)可以看作是fdevent這個(gè)結(jié)構(gòu)體對(duì)外暴露的接口, 也就是OO中所謂的類(lèi)public函數(shù), fdevent.c則是這些函數(shù)的實(shí)現(xiàn),而以fdevent_為開(kāi)頭的幾個(gè)C文件則是不同的網(wǎng)絡(luò)IO模型的實(shí)現(xiàn),比如fdevent_select.c文件是select模型的實(shí)現(xiàn).我不打算對(duì)各種類(lèi)型的網(wǎng)絡(luò)IO模型做詳細(xì)的介紹,事實(shí)上,所有這里用到的網(wǎng)絡(luò)IO模型,我只用過(guò)select和epoll,所以我打算以select模型為例展開(kāi)這里的討論,因?yàn)閟elect是相對(duì)而言用的最多也是大多數(shù)人在學(xué)習(xí)多路復(fù)用IO的時(shí)候?qū)W到的第一個(gè)模型,即使在epoll橫行的今天,select模型仍然有著它的一席之地.

            1)初始化
            如何配置使用的是哪種網(wǎng)絡(luò)IO模型?在配置文件中有一項(xiàng)server.event-handler就是配置需要使用的網(wǎng)絡(luò)IO的,比如server.event-handler="select"就是選擇select, 其它的配置字符串參見(jiàn)前一節(jié)最開(kāi)始提到的那些類(lèi)型.服務(wù)器在初始化的時(shí)候讀取該配置項(xiàng), 將網(wǎng)絡(luò)IO事件類(lèi)型存放在結(jié)構(gòu)體server的成員event_handler中.
            接著, 在server.c的main函數(shù)中服務(wù)器調(diào)用fdevent_init(size_t maxfds, fdevent_handler_t type)初始化一個(gè)fdevents指針, 返回的結(jié)果存放在server結(jié)構(gòu)體中的ev成員中.
            在這個(gè)函數(shù)中, 根據(jù)type參數(shù)進(jìn)行初始化, 生成具體各種不同類(lèi)型的fdevents指針, 這些初始化的函數(shù)都是以init為后綴的, 而所有具體實(shí)現(xiàn)的文件名為
            fdevent_***.c(如fdevent_select.c是select模型的實(shí)現(xiàn)), 對(duì)外暴露的僅僅是那個(gè)以init為后綴的函數(shù), 而上面那些函數(shù)接口的實(shí)現(xiàn)全都是這些文件中
            靜態(tài)函數(shù), 很好的限制了它們的使用范圍, 做到了信息隱藏, 這些函數(shù)可以看作是類(lèi)中的私有函數(shù), 以select模型為例:
            對(duì)外暴露的初始化函數(shù)是fdevent_select_init, 它在fdevent.h中聲明, 也就是說(shuō)這個(gè)函數(shù)是對(duì)外暴露的, 而這個(gè)函數(shù)在fdevent_select.c被定義:
            int fdevent_select_init(fdevents *ev) {
                ev
            ->type = FDEVENT_HANDLER_SELECT;
            #define SET(x) \
                ev
            ->= fdevent_select_##x;

                SET(reset);
                SET(poll);

                SET(event_del);
                SET(event_add);

                SET(event_next_fdndx);
                SET(event_get_fd);
                SET(event_get_revent);

                
            return 0;
            }
            查看fdevent_secelt.c文件,可以看到,名為fdevent_select_***的函數(shù)都是這個(gè)文件的靜態(tài)函數(shù), 再?gòu)拿嫦驅(qū)ο蟮挠^點(diǎn)出發(fā),這些函數(shù)屬于采用select模型實(shí)現(xiàn)的fdevent的"私有函數(shù)", 如此做法, 很好的滿(mǎn)足了所謂的"信息隱藏".

            2) 使用
            在服務(wù)器創(chuàng)建一個(gè)socket fd并且進(jìn)行監(jiān)聽(tīng)后, 要將該fd注冊(cè)到fdevent中, 這樣才能使用使用這個(gè)事件處理機(jī)制.
            在server.c文件的main函數(shù)中, 調(diào)用network_register_fdevents函數(shù)將所有監(jiān)聽(tīng)的fd注冊(cè)到事件處理器中:
            int network_register_fdevents(server *srv) {
                size_t i;

                
            if (-1 == fdevent_reset(srv->ev)) {
                    
            return -1;
                }

                
            /* register fdevents after reset */
                
            for (i = 0; i < srv->srv_sockets.used; i++) {
                    server_socket 
            *srv_socket = srv->srv_sockets.ptr[i];

                    fdevent_register(srv
            ->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket);
                    fdevent_event_add(srv
            ->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN);
                }
                
            return 0;
            }


            關(guān)鍵是在循環(huán)體中的兩個(gè)函數(shù), fdevent_register的第三個(gè)參數(shù)是一個(gè)回調(diào)函數(shù), 就是fdevents的成員fdarray中每個(gè)fdnode的成員handler:
            int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) {
                fdnode 
            *fdn;

                
            // 分配一個(gè)fdnode指針
                fdn = fdnode_init();
               
                
            // 保存回調(diào)函數(shù)
                fdn->handler = handler;
                
            // 保存fd
                fdn->fd      = fd;
                
            // 保存context 對(duì)server是server為socket指針, 對(duì)client是connection指針
                fdn->ctx     = ctx;

                
            // 以fd為索引在fdarray中保存這個(gè)fdnode
                ev->fdarray[fd] = fdn;

                
            return 0;
            }

            這里有一個(gè)小技巧, 函數(shù)中的倒數(shù)第二行, 以fd為索引保存fdnode, 因?yàn)檫@里的fdarray是一個(gè)數(shù)組, 因此這個(gè)方法可以以O(shè)(1)的速度找到與該fd相關(guān)的fdnode指針.但是, 因?yàn)?,1,2這三個(gè)fd已經(jīng)提前預(yù)留給了標(biāo)準(zhǔn)輸入輸出錯(cuò)誤這三個(gè)IO, 所以采用這樣的算法將會(huì)至少浪費(fèi)三個(gè)fdnode指針.

            現(xiàn)在, 可以對(duì)fdnode結(jié)構(gòu)體中兩個(gè)成員進(jìn)一步進(jìn)行解析了:
                fdevent_handler handler;
                void *ctx;
            其中, 如果該fd是服務(wù)器監(jiān)聽(tīng)客戶(hù)端連接的fd, 那么handler = network_server_handle_fdevent(在network.c文件中), ctx保存的就是server指針;
            如果該fd是accapt客戶(hù)端連接之后的fd, 那么handler = connection_handle_fdevent(在connections.c文件中), ctx保存的就是connection指針.

            回過(guò)頭來(lái)看,在將服務(wù)器監(jiān)聽(tīng)fd注冊(cè)到網(wǎng)絡(luò)IO事件處理器中之后, 這個(gè)處理器就要開(kāi)始循環(huán)處理了, 在server.c中的main.c函數(shù)中是這個(gè)輪詢(xún)的主過(guò)程:
                    // 輪詢(xún)FD
                    if ((n = fdevent_poll(srv->ev, 1000)) > 0) {
                        
            /* n is the number of events */
                        
            int revents;
                        
            int fd_ndx;

                        fd_ndx 
            = -1;
                        
            do {
                            fdevent_handler handler;
                            
            void *context;
                            handler_t r;

                            
            // 獲得處理這些事件的函數(shù)指針 fd等

                            
            // 獲得下一個(gè)fd在fdarray中的索引
                            fd_ndx  = fdevent_event_next_fdndx (srv->ev, fd_ndx);
                            
            // 獲得這個(gè)fd要處理的事件類(lèi)型
                            revents = fdevent_event_get_revent (srv->ev, fd_ndx);
                            
            // 獲取fd
                            fd      = fdevent_event_get_fd     (srv->ev, fd_ndx);
                            
            // 獲取回調(diào)函數(shù)
                            handler = fdevent_get_handler(srv->ev, fd);
                            
            // 獲取處理相關(guān)的context(對(duì)server是server_socket指針, 對(duì)client是connection指針)
                            context = fdevent_get_context(srv->ev, fd);

                            
            /* connection_handle_fdevent needs a joblist_append */
                            
            // 進(jìn)行處理
                            switch (r = (*handler)(srv, context, revents)) {
                            
            case HANDLER_FINISHED:
                            
            case HANDLER_GO_ON:
                            
            case HANDLER_WAIT_FOR_EVENT:
                            
            case HANDLER_WAIT_FOR_FD:
                                
            break;
                            
            case HANDLER_ERROR:
                                
            /* should never happen */
                                SEGFAULT();
                                
            break;
                            
            default:
                                log_error_write(srv, __FILE__, __LINE__, 
            "d", r);
                                
            break;
                            }
                        } 
            while (--> 0);
            簡(jiǎn)單的說(shuō), 這個(gè)過(guò)程就是:首先調(diào)用poll函數(shù)指針獲取相關(guān)網(wǎng)絡(luò)IO被觸發(fā)的事件數(shù), 保存在整型變量n中, 然后根據(jù)這個(gè)n值進(jìn)行以下循環(huán), 每次處理完n值減一, 為0之后退出, 這個(gè)循環(huán)的大致過(guò)程是: 首先獲取下一個(gè)被觸發(fā)的網(wǎng)絡(luò)事件在fdnode數(shù)組中的索引, 接著根據(jù)該索引獲取相關(guān)的事件類(lèi)型, fd, 回調(diào)函數(shù), contex, ,接著根據(jù)這些調(diào)用回調(diào)函數(shù)(也就是我們上面提到的函數(shù)
            network_server_handle_fdevent和connection_handle_fdevent),請(qǐng)注意, 在本節(jié)的最開(kāi)始部分曾經(jīng)提到過(guò)fdevent.h中聲明的函數(shù)都是對(duì)外暴露的fdevent結(jié)構(gòu)體"public函數(shù)", 在上面這個(gè)輪詢(xún)的過(guò)程中使用的正是這些"public函數(shù)", 在這些"public函數(shù)"中再根據(jù)曾經(jīng)初始化的函數(shù)指針進(jìn)行調(diào)用, 實(shí)現(xiàn)了OO中所謂的"多態(tài)".

            以上就是通過(guò)fdevent結(jié)構(gòu)體實(shí)現(xiàn)的網(wǎng)絡(luò)IO處理器模型, 在這里體現(xiàn)如何使用C實(shí)現(xiàn)OO面向?qū)ο缶幊痰姆N種常用技巧,不放在本節(jié)最后做一個(gè)總結(jié):
            1) fdevent結(jié)構(gòu)體是一個(gè)虛擬基類(lèi), 其中的函數(shù)指針就是虛擬基類(lèi)中的純虛函數(shù), 由具體實(shí)現(xiàn)去初始化之.fdevent結(jié)構(gòu)體中的對(duì)象為所有派生類(lèi)的公共成員, 而用各個(gè)預(yù)編譯宏包圍的成員則是各個(gè)派生類(lèi)的私有成員.

            2) 在fdevent.h中聲明的函數(shù)可以理解為虛擬基類(lèi)對(duì)外暴露的接口, 也就是public函數(shù).

            3) 各個(gè)具體的實(shí)現(xiàn)分別是各個(gè)實(shí)現(xiàn)C文件中的靜態(tài)函數(shù), 也就是派生類(lèi)的private函數(shù).

            如果閱讀到這里仍然對(duì)lighttpd中網(wǎng)絡(luò)IO處理器模型有疑問(wèn), 可以具體參看前面提到的fdevent.h/c文件, 以及以fdevent_為前綴的c文件.

            posted on 2008-08-28 23:20 那誰(shuí) 閱讀(4159) 評(píng)論(2)  編輯 收藏 引用 所屬分類(lèi): 網(wǎng)絡(luò)編程服務(wù)器設(shè)計(jì)Linux/Unixlighttpd

            評(píng)論

            # re: lighttpd1.4.18代碼分析(三)--網(wǎng)絡(luò)IO事件處理器的使用  回復(fù)  更多評(píng)論   

            分析的不錯(cuò)
            2008-08-29 10:11 |

            # re: lighttpd1.4.18代碼分析(三)--網(wǎng)絡(luò)IO事件處理器的使用[未登錄](méi)  回復(fù)  更多評(píng)論   

            謝謝博主的分析,寫(xiě)的很到位
            2008-08-29 18:50 | 鄒從杰
            久久久久久久91精品免费观看| 久久精品9988| 久久亚洲精精品中文字幕| 国内精品久久久久久99蜜桃| 热久久国产精品| 亚洲午夜精品久久久久久app| 99精品久久精品一区二区| 91秦先生久久久久久久| 色综合久久久久综合体桃花网| 青青草国产精品久久| 精品久久久一二三区| 麻豆精品久久精品色综合| 久久SE精品一区二区| 精品无码久久久久久久久久| 久久夜色精品国产噜噜麻豆| 欧美色综合久久久久久| 国产精品一区二区久久精品| 久久综合综合久久综合| 久久频这里精品99香蕉久| 一本大道久久a久久精品综合| 久久精品国产亚洲av水果派| 一本色综合久久| 婷婷久久五月天| 久久精品成人一区二区三区| 狠色狠色狠狠色综合久久| 久久久亚洲欧洲日产国码二区| 日日狠狠久久偷偷色综合0| 国产69精品久久久久9999| 久久精品视频网| 免费观看久久精彩视频| 久久国产精品成人免费| 久久综合久久综合久久| 久久国产精品成人免费 | 久久精品国产精品亚洲下载| 精品国际久久久久999波多野| 伊人色综合久久天天人手人婷 | 香蕉99久久国产综合精品宅男自| 久久久久亚洲精品中文字幕| 久久精品二区| 蜜桃麻豆WWW久久囤产精品| 久久精品一本到99热免费|