青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

那誰的技術(shù)博客

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

lighttpd1.4.18代碼分析(六)--處理連接fd的流程

現(xiàn)在開始講解lighttpd如何處理連接fd.

第四節(jié),已經(jīng)講解了如何處理服務(wù)器負(fù)責(zé)監(jiān)聽連接的fd,處理連接fd的流程在前半部分與監(jiān)聽fd大體相同:在通過監(jiān)聽fd接收一個(gè)新的連接之后,服務(wù)器將這個(gè)fd加入到事件處理器中, 設(shè)置它所感興趣的IO事件類型, 當(dāng)IO狀態(tài)發(fā)生變化時(shí), 事件處理器獲取被觸發(fā)的fd,回調(diào)函數(shù),事件等,然后通過已經(jīng)注冊的回調(diào)函數(shù)進(jìn)行處理.

這里的區(qū)別就在于回調(diào)函數(shù)的不同上.對于監(jiān)聽fd而言, 該回調(diào)函數(shù)是network.c文件中的network_server_handle_fdevent函數(shù),這個(gè)在第四節(jié)中已經(jīng)做了分析;與之對應(yīng)的,連接fd的回調(diào)函數(shù)是connections.c文件中的connection_handle_fdevent函數(shù).

// 這個(gè)函數(shù)是處理接受鏈接的函數(shù), 與network_server_handle_fdevent對應(yīng)
handler_t connection_handle_fdevent(void *s, void *context, int revents) {
    server     
*srv = (server *)s;
    connection 
*con = context;

    
// 添加到server的joblist中
    joblist_append(srv, con);

    
// 可讀
    if (revents & FDEVENT_IN) {
        con
->is_readable = 1;
    }
    
    
// 可寫
    if (revents & FDEVENT_OUT) {
        con
->is_writable = 1;
        
/* we don't need the event twice */
    }

    
// 既不可讀也不可寫, 可能是出錯(cuò)了
    if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) {
        
/* looks like an error */

        
/* FIXME: revents = 0x19 still means that we should read from the queue */
        
if (revents & FDEVENT_HUP) {
            
if (con->state == CON_STATE_CLOSE) {
                con
->close_timeout_ts = 0;
            } 
else {
                
/* sigio reports the wrong event here
                 *
                 * there was no HUP at all
                 
*/
#ifdef USE_LINUX_SIGIO
                
if (srv->ev->in_sigio == 1) {
                    log_error_write(srv, __FILE__, __LINE__, 
"sd",
                        
"connection closed: poll() -> HUP", con->fd);
                } 
else {
                    connection_set_state(srv, con, CON_STATE_ERROR);
                }
#else
                connection_set_state(srv, con, CON_STATE_ERROR);
#endif

            }
        } 
else if (revents & FDEVENT_ERR) {
#ifndef USE_LINUX_SIGIO
            log_error_write(srv, __FILE__, __LINE__, 
"sd",
                    
"connection closed: poll() -> ERR", con->fd);
#endif
            connection_set_state(srv, con, CON_STATE_ERROR);
        } 
else {
            log_error_write(srv, __FILE__, __LINE__, 
"sd",
                    
"connection closed: poll() -> ???", revents);
        }
    }

    
// 如果連接的狀態(tài)是READ, 那么處理read狀態(tài)
    if (con->state == CON_STATE_READ ||
        con
->state == CON_STATE_READ_POST) {
        connection_handle_read_state(srv, con);
    }

    
// 如果連接狀態(tài)是WRITE并卻寫緩沖區(qū)隊(duì)列中不為空 并且該連接是可寫的, 就去處理寫狀態(tài)
    if (con->state == CON_STATE_WRITE &&
        
!chunkqueue_is_empty(con->write_queue) &&
        con
->is_writable) {

        
if (-1 == connection_handle_write(srv, con)) {
            connection_set_state(srv, con, CON_STATE_ERROR);

            log_error_write(srv, __FILE__, __LINE__, 
"ds",
                    con
->fd,
                    
"handle write failed.");
        } 
else if (con->state == CON_STATE_WRITE) {
            con
->write_request_ts = srv->cur_ts;
        }
    }

    
// 如果連接的狀態(tài)是關(guān)閉
    if (con->state == CON_STATE_CLOSE) {
        
/* flush the read buffers */
        
int b;

        
// 檢查讀緩沖區(qū)中是否還有數(shù)據(jù)
        if (ioctl(con->fd, FIONREAD, &b)) {
            log_error_write(srv, __FILE__, __LINE__, 
"ss",
                    
"ioctl() failed", strerror(errno));
        }

        
// 如果還有數(shù)據(jù), 打印錯(cuò)誤log, 并且讀入數(shù)據(jù)
        if (b > 0) {
            
char buf[1024];
            log_error_write(srv, __FILE__, __LINE__, 
"sdd",
                    
"CLOSE-read()", con->fd, b);

            
/* */
            read(con
->fd, buf, sizeof(buf));
        } 
else {
            
/* nothing to read */

            con
->close_timeout_ts = 0;
        }
    }

    
    
return HANDLER_FINISHED;
}
在結(jié)構(gòu)體connection,也就是保存連接相關(guān)數(shù)據(jù)的結(jié)構(gòu)體中, 有一個(gè)叫state的成員, 顧名思義, 這個(gè)成員保存的是一個(gè)連接的狀態(tài),這里所說的狀態(tài)與前面提到的IO狀態(tài)是不同, IO狀態(tài)是用于表示一個(gè)fd可讀/可寫/出錯(cuò)等, 而這個(gè)狀態(tài)更多的是與協(xié)議相關(guān)的部分.它是一個(gè)枚舉類型:
/* the order of the items should be the same as they are processed
 * read before write as we use this later 
*/
typedef 
enum {
    CON_STATE_CONNECT,            
// 連接
    CON_STATE_REQUEST_START,    // 開始獲取請求
    CON_STATE_READ,                // 處理讀
    CON_STATE_REQUEST_END,        // 請求結(jié)束
    CON_STATE_READ_POST,        // 處理讀,但是是POST過來的數(shù)據(jù)
    CON_STATE_HANDLE_REQUEST,    // 處理請求
    CON_STATE_RESPONSE_START,    // 開始回復(fù)
    CON_STATE_WRITE,            // 處理寫
    CON_STATE_RESPONSE_END,        // 回復(fù)結(jié)束
    CON_STATE_ERROR,            // 出錯(cuò)
    CON_STATE_CLOSE                // 連接關(guān)閉
} connection_state_t;

為什么需要這些狀態(tài)?因?yàn)閘ighttpd中采用了所謂"狀態(tài)機(jī)"去處理連接,而這些狀態(tài)就是狀態(tài)機(jī)中的各種不同狀態(tài).
在lighttpd的官方文檔中, 對其使用的狀態(tài)機(jī)有一篇文檔,在這里:
http://redmine.lighttpd.net/wiki/lighttpd/Docs:InternalHTTPStates

我覺得里面的這幅圖非常的直觀,學(xué)習(xí)過編譯原理的人一看就可以知道這是狀態(tài)機(jī)的轉(zhuǎn)換圖:


現(xiàn)在回到本章的主題中, lighttpd如何處理連接fd.
前面給出的處理連接fd的回調(diào)函數(shù),最開始地方有一段代碼:
    joblist_append(srv, con);
這個(gè)函數(shù)將一個(gè)連接connection結(jié)構(gòu)體放入到j(luò)oblist中, 后面的部分根據(jù)不同的情況設(shè)置connection中的state字段,調(diào)用的是connection_set_state函數(shù).

現(xiàn)在回到server.c函數(shù)中, 第四節(jié)中已經(jīng)結(jié)合處理監(jiān)聽fd的流程講解了這段函數(shù):
        // 輪詢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要處理的事件類型
                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(對server是server_socket指針, 對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);
        } 
else if (n < 0 && errno != EINTR) {
            log_error_write(srv, __FILE__, __LINE__, 
"ss",
                    
"fdevent_poll failed:",
                    strerror(errno));
        }

在server.c文件中, 緊跟著這段代碼的是:
        // 處理joblist中的連接
        for (ndx = 0; ndx < srv->joblist->used; ndx++) {
            connection 
*con = srv->joblist->ptr[ndx];
            handler_t r;

            connection_state_machine(srv, con);

            
switch(r = plugins_call_handle_joblist(srv, con)) {
            
case HANDLER_FINISHED:
            
case HANDLER_GO_ON:
                
break;
            
default:
                log_error_write(srv, __FILE__, __LINE__, 
"d", r);
                
break;
            }

            con
->in_joblist = 0;
        }
簡單的說, 這段代碼是一個(gè)循環(huán), 從joblist中依次取出已經(jīng)放在這里的connection指針, 再調(diào)用connection_state_machine函數(shù)進(jìn)行處理.

connection_state_machine函數(shù)是一個(gè)非常重要的函數(shù), 它就是處理連接fd的狀態(tài)機(jī), 在后面將詳細(xì)分析這個(gè)函數(shù).

現(xiàn)在回顧一下lighttpd處理連接fd的大體框架:前面的流程與處理監(jiān)聽fd大體相同,在與處理連接fd相關(guān)的回調(diào)函數(shù)中, 首先將需要處理的fd相關(guān)的connection加入到j(luò)oblist中, 設(shè)置它的state, 后面再輪詢joblist, 進(jìn)入狀態(tài)機(jī)進(jìn)行處理.

這個(gè)過程可能值得商榷, 比如為什么在回調(diào)函數(shù)中需要將一個(gè)connection指針加入到j(luò)oblist中, 后面再一個(gè)循環(huán)輪詢joblist中的connection,這樣不是顯得效率低下嗎?我們需要注意的是,前面提到的IO事件狀態(tài)和connection中的成員state是不同的!第一個(gè)輪詢過程(IO事件處理器的輪詢)是根據(jù)哪些fd的IO發(fā)生了變化被觸發(fā)而去調(diào)用回調(diào)函數(shù), 而后面的循環(huán)(joblist的輪詢)中的connection則不一定都是IO發(fā)生變化的!

打一個(gè)比方, 一個(gè)連接到來, 此時(shí)我們把它放入到事件處理器中, 當(dāng)它可讀時(shí)被觸發(fā), 也就是在第一個(gè)輪詢中被觸發(fā);如果在處理的時(shí)候出了問題, 不能繼續(xù), 此時(shí)我們只需要保存它當(dāng)前的state字段, 在下一次操作中, 由于它的IO沒有發(fā)生變化, 那么將不會(huì)在第一個(gè)輪詢也就是IO事件處理器中被處理, 而只會(huì)在輪詢joblist時(shí)被處理, 只需要它的state字段是正確的, 放到狀態(tài)機(jī)處理函數(shù)中就可以繼續(xù)下去.


posted on 2008-09-19 10:46 那誰 閱讀(3737) 評論(1)  編輯 收藏 引用 所屬分類: 網(wǎng)絡(luò)編程 、服務(wù)器設(shè)計(jì) 、Linux/Unix 、lighttpd

評論

# re: lighttpd1.4.18代碼分析(六)--處理連接fd的流程  回復(fù)  更多評論   

“在下一次操作中, 由于它的IO沒有發(fā)生變化, 那么將不會(huì)在第一個(gè)輪詢也就是IO事件處理器中被處理, 而只會(huì)在輪詢joblist時(shí)被處理”

這句容易誤導(dǎo)讀者,應(yīng)改為"輪詢完畢(io操作完成,可能設(shè)置status)后的joblist循環(huán)里繼續(xù)處理status

2010-03-26 15:55 | sunceenjoy
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            精品成人一区二区| 欧美一区二区在线观看| 欧美一区二区视频在线观看| 中日韩高清电影网| 亚洲午夜精品久久久久久app| 一区二区三区免费观看| 亚洲一区三区视频在线观看| 亚洲欧美日韩区 | 亚洲图片欧洲图片av| 亚洲美女免费精品视频在线观看| 亚洲激情视频在线| 欧美专区第一页| 久久精品视频在线播放| 久久香蕉国产线看观看av| 免费成人av在线| 欧美三区不卡| 精品动漫3d一区二区三区免费| 亚洲黄色成人| 午夜久久福利| 蘑菇福利视频一区播放| 亚洲精品欧美激情| 欧美在线日韩在线| 欧美日韩国产小视频在线观看| 国产欧美一区二区三区国产幕精品| 国产专区精品视频| 夜夜躁日日躁狠狠久久88av| 久久久久久黄| 日韩五码在线| 久久综合九色| 国产欧美日韩精品丝袜高跟鞋| 亚洲国产精品成人综合色在线婷婷 | 久久麻豆一区二区| 日韩性生活视频| 久久综合一区二区| 国产精品亚洲视频| 一区二区三区产品免费精品久久75| 久久久久久久久久久久久9999 | 久久激情五月丁香伊人| 欧美先锋影音| 日韩午夜三级在线| 免费欧美电影| 久久狠狠亚洲综合| 欧美日韩美女一区二区| 91久久精品网| 亚洲视频免费| 性做久久久久久久免费看| 久久久久国产精品麻豆ai换脸| 老鸭窝亚洲一区二区三区| 国产精品久久久久久久午夜片| 亚洲国产一区二区三区青草影视 | 久久国产一区二区| 亚洲激情在线观看视频免费| 亚洲一区视频在线观看视频| 欧美激情一区二区三区四区| 亚洲国产成人一区| 葵司免费一区二区三区四区五区| 亚洲在线成人精品| 国产精品久久久久久久久久ktv| 免费一区视频| 久久综合成人精品亚洲另类欧美| 中文在线资源观看视频网站免费不卡| 欧美激情1区2区3区| 亚洲国产二区| 欧美国产高清| 欧美电影免费观看高清完整版| 精品不卡视频| 免费不卡中文字幕视频| 噜噜噜久久亚洲精品国产品小说| 影音国产精品| 欧美国产日韩二区| 免费欧美电影| 一区二区三区四区五区在线| 在线亚洲一区观看| 国产色视频一区| 美女脱光内衣内裤视频久久影院| 蜜乳av另类精品一区二区| 亚洲人成在线观看| 亚洲精品少妇网址| 国产精品夜夜夜| 久久夜精品va视频免费观看| 久久久噜噜噜久久人人看| 亚洲国产一区视频| 国产精品99久久久久久久vr | 国产精品一区二区三区久久久| 午夜免费日韩视频| 久久精品av麻豆的观看方式| 亚洲国产欧美不卡在线观看| 亚洲美女av电影| 国产手机视频一区二区| 免费在线看一区| 欧美日韩国产精品一区二区亚洲| 欧美一级夜夜爽| 免费在线亚洲| 欧美一区三区三区高中清蜜桃| 久久久久9999亚洲精品| 99在线精品视频在线观看| 午夜精品久久久久久久久久久久久| 永久久久久久| 一本一道久久综合狠狠老精东影业 | 日韩亚洲视频在线| 国产精品一区视频| 欧美成人一品| 国产美女精品在线| 亚洲三级免费电影| 精品成人一区二区| 亚洲小少妇裸体bbw| 亚洲国产婷婷香蕉久久久久久| 亚洲性线免费观看视频成熟| 亚洲三级电影全部在线观看高清| 亚洲女人天堂av| 9l国产精品久久久久麻豆| 欧美一区二区三区电影在线观看| 一区二区三区精品国产| 久久国产精品亚洲77777| 亚洲视频一区二区免费在线观看| 欧美激情综合五月色丁香| 欧美日韩国产一区精品一区| 亚洲福利视频专区| 亚洲第一中文字幕在线观看| 免费日韩视频| 亚洲国产精彩中文乱码av在线播放| 嫩模写真一区二区三区三州| 欧美午夜不卡在线观看免费| 欧美一区1区三区3区公司| 欧美有码在线视频| 亚洲午夜视频在线| 欧美精品尤物在线| 亚洲国产另类精品专区 | 国产亚洲精品aa午夜观看| 亚洲精选在线观看| 亚洲三级免费电影| 免费日韩av| 免费视频一区| 亚洲高清av在线| 久久免费精品视频| 久久久综合香蕉尹人综合网| 91久久中文| 一区二区三区在线不卡| 午夜精品一区二区三区在线| 亚洲一区美女视频在线观看免费| 欧美久久电影| 亚洲精品久久久久久久久| 91久久精品网| 欧美精品久久久久久久| 亚洲国产精品久久精品怡红院| 亚洲第一精品夜夜躁人人爽| 久久久五月天| 欧美国产日本韩| 一区二区免费在线观看| 国产精品白丝av嫩草影院| 一本久久综合亚洲鲁鲁| 午夜精品一区二区三区四区 | 午夜精品久久久久99热蜜桃导演| 午夜久久久久久| 国产综合一区二区| 久久综合免费视频影院| 亚洲人线精品午夜| 亚洲欧美国产va在线影院| 国产香蕉久久精品综合网| 久久免费观看视频| 亚洲精品国久久99热| 午夜在线a亚洲v天堂网2018| 国产一区二区剧情av在线| 老司机午夜精品视频| 日韩视频在线免费| 久久国产精品一区二区三区四区| 激情视频一区| 欧美色一级片| 久久久精品性| 夜夜嗨av色综合久久久综合网| 欧美日韩国产色综合一二三四| 亚洲一区二区在线看| 欧美插天视频在线播放| 亚洲一区二区三区四区五区黄| 国产精品午夜春色av| 老司机精品久久| 亚洲一区二区三区乱码aⅴ| 免费不卡在线观看av| 亚洲综合国产激情另类一区| 在线精品观看| 国产精品日本一区二区| 你懂的视频一区二区| 亚洲一区在线播放| 亚洲福利视频三区| 欧美在线观看一二区| 日韩网站在线观看| 国产综合18久久久久久| 欧美日精品一区视频| 久久综合五月| 欧美亚洲自偷自偷| 一区二区三区 在线观看视频| 久久综合电影| 国产精品久久九九| 欧美va天堂va视频va在线| 亚洲欧美日韩直播| 在线视频精品一| 亚洲欧洲一区二区在线观看| 理论片一区二区在线| 久久精品国产在热久久| 亚洲欧美日韩综合一区|