日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Lighttpd源码分析之状态机与插件

發布時間:2025/3/21 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Lighttpd源码分析之状态机与插件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Lighttpd啟動時完成了一系列初始化操作后,就進入了一個包含11個狀態的有限狀態機中。

每個連接都是一個connection實例(con),狀態的切換取決于con->state。

lighttpd經過初步處理后將con的基本信息初始化,而插件對事件的處理就是針對con進行的,它拿到con后按照業務需要進行相應處理,然后再交還給lighttpd,lighttpd根據con中的信息完成響應。

狀態定義如下:

[cpp] view plain copy
  • typedef?enum??
  • {??
  • ????CON_STATE_CONNECT,?????????????//connect?連接開始??
  • ?????CON_STATE_REQUEST_START,?????//reqstart?開始讀取請求??
  • ?????CON_STATE_READ,?????????????//read?讀取并解析請求??
  • ?????CON_STATE_REQUEST_END,?????????//reqend?讀取請求結束??
  • ?????CON_STATE_READ_POST,?????????//readpost?讀取post數據??
  • ?????CON_STATE_HANDLE_REQUEST,?????//handelreq?處理請求??
  • ????CON_STATE_RESPONSE_START,?????//respstart?開始回復??
  • ????CON_STATE_WRITE,?????????????//write?回復寫數據??
  • ????CON_STATE_RESPONSE_END,?????//respend?回復結束??
  • ????CON_STATE_ERROR,?????????????//error?出錯??
  • ????CON_STATE_CLOSE?????????????//close?連接關閉??
  • }?connection_state_t;??
  • 下面就是lighttpd的狀態機:

    在每個連接中都會保存這樣一個狀態機,用以表示當前連接的狀態。

    在連接建立以后,在connections.c/connection_accpet()函數中,lighttpd調用connection_set_state()函數,將新建立的連接的狀態設置為CON_STATE_REQUEST_START。在這個狀態中,lighttpd記錄連接建立的時間等信息。

    整個狀態機的核心函數是connections.c/ connection_state_machine()函數。

    函數的主體部分刪減之后如下:

    [cpp] view plain copy
  • int?connection_state_machine(server?*?srv,?connection?*?con)??
  • {??
  • ????int?done?=?0,?r;??
  • ????while?(done?==?0)??
  • ????{??
  • ????????size_t?ostate?=?con?->?state;??
  • ????????int?b;??
  • ????????//根據當前狀態機的狀態進行相應的處理和狀態轉換。??
  • ????????switch?(con->state)??
  • ????????{??
  • ????????case?CON_STATE_REQUEST_START:????/*?transient?*/??
  • ????????//do?something??
  • ????????case?CON_STATE_REQUEST_END:????/*?transient?*/??
  • ????????//do?something??
  • ????????case?CON_STATE_HANDLE_REQUEST:??
  • ????????//do?something??
  • ????????case?CON_STATE_RESPONSE_START:??
  • ????????//do?something??
  • ????????case?CON_STATE_RESPONSE_END:????/*?transient?*/??
  • ????????//do?something??
  • ????????case?CON_STATE_CONNECT:??
  • ????????//do?something??
  • ????????case?CON_STATE_CLOSE:??
  • ????????//do?something??
  • ????????case?CON_STATE_READ_POST:??
  • ????????//do?something??
  • ????????case?CON_STATE_READ:??
  • ????????//do?something??
  • ????????case?CON_STATE_WRITE:??
  • ????????//do?something??
  • ????????case?CON_STATE_ERROR:????/*?transient?*/??
  • ????????//do?something??
  • ????????default:??
  • ????????//do?something??
  • ????????????break;??
  • ????????}//end?of?switch(con?->?state)?...??
  • ????????if?(done?==?-1)??
  • ????????{??
  • ????????????done?=?0;??
  • ????????}??
  • ????????else?if?(ostate?==?con->state)??
  • ????????{??
  • ????????????done?=?1;??
  • ????????}??
  • ????}??
  • ????/*?something?else?*/??
  • ??
  • ????/*?將fd加入到fdevent系統中,等待IO事件。?
  • ?????*?當有數據可讀的時候,在main函數中,lighttpd調用這個fd對應的handle函數,?
  • ?????*?這里就是connection_handle_fdevent()函數。?
  • ?????*?這個函數一開始將連接加入到了joblist(作業隊列)中。?
  • ?????*/??
  • ????switch?(con->state)??
  • ????{??
  • ????case?CON_STATE_READ_POST:??
  • ????case?CON_STATE_READ:??
  • ????case?CON_STATE_CLOSE:??
  • ????????fdevent_event_add(srv->ev,?&(con->fde_ndx),?con->fd,?FDEVENT_IN);??
  • ????????break;??
  • ????case?CON_STATE_WRITE:??
  • ????????/*?request?write-fdevent?only?if?we?really?need?it?
  • ?????????*?-?if?we?have?data?to?write?
  • ?????????*?-?if?the?socket?is?not?writable?yet?
  • ?????????*/??
  • ????????if?(!chunkqueue_is_empty(con->write_queue)?&&??
  • ????????????(con->is_writable?==?0)&&?(con->traffic_limit_reached?==?0))??
  • ????????{??
  • ????????????fdevent_event_add(srv->ev,?&(con->fde_ndx),?con->fd,?FDEVENT_OUT);??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????fdevent_event_del(srv->ev,?&(con->fde_ndx),?con->fd);??
  • ????????}??
  • ????????break;??
  • ????default:??
  • ????????fdevent_event_del(srv->ev,?&(con->fde_ndx),?con->fd);??
  • ????????break;??
  • ????}??
  • ????return?0;??
  • }??

  • 這個函數首先根據當前的狀態進入對應的switch分支執行相應的動作,然后根據情況進入下一個狀態。

    跳出switch語句之后,如果連接的狀態沒有改變,說明連接讀寫數據還沒有結束,但是需要等待IO事件,這時跳出循環,等待IO事件。

    如果在處理的過程中不需要等待IO事件,那么在while循環中,連接將被處理完畢并關閉。

    在我們的main函數中,之前討論過,在一個while循環中,處理超時,處理IO時間,之后有下面這段代碼:

    [cpp] view plain copy
  • 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;??
  • ??}??

  • 這段代碼對joblist中的所有連接依次調用connection_state_machine()函數進行處理。

    下面說明下各狀態的主要內容:

    [cpp] view plain copy
  • CON_STATE_CONNECT??
  • 清除待讀取隊列中的數據-chunkqueue_reset(con->read_queue);??
  • 置con->request_count?=?0。(本次連接還未處理過請求)??
  • CON_STATE_REQUEST_START??/*transient?*/??
  • 記錄事件起始時間;??
  • con->request_count++(一次長連接最多可以處理的請求數量是有限制的);??
  • 轉移到CON_STATE_READ狀態。??
  • ??
  • CON_STATE_READ和CON_STATE_READ_POST??
  • connection_handle_read_state(srv,con);??
  • CON_STATE_REQUEST_END????/*transient?*/??
  • http_request_parse(srv,?con);??
  • 解析請求,若是POST請求則轉移到CON_STATE_READ_POST狀態,??
  • 否則轉移到CON_STATE_HANDLE_REQUEST狀態。??
  • CON_STATE_HANDLE_REQUEST??
  • http_response_prepare(srv,?con);??
  • 函數中調用??
  • handle_uri_raw;??
  • handle_uri_clean;??
  • handle_docroot;??
  • handle_physical;??
  • handle_subrequest_start;??
  • handle_subrequest。??
  • 如果函數返回了HANDLER_FINISHED,且con->mode!=DIRECT(事件已經被我們的業務插件接管),??
  • 則直接進入CON_STATE_RESPONSE_START。??
  • 否則lighttpd會做一些處理后再進入CON_STATE_RESPONSE_START狀態。??
  • 如果函數返回了HANDLER_WAIT_FOR_FD或??
  • HANDLER_WAIT_FOR_EVENT,??
  • 狀態依舊會停留在CON_STATE_HANDLE_REQUEST,等待事件或數據。??
  • 如果函數返回了HANDLER_ERROR,進入到CON_STATE_ERROR狀態。??
  • CON_STATE_RESPONSE_START??
  • connection_handle_write_prepare(srv,con);??
  • CON_STATE_WRITE??
  • connection_handle_write(srv,con);??
  • CON_STATE_RESPONSE_END??
  • 調用插件的handle_request_done接口。??
  • 如果是長連接,重新回到CON_STATE_REQUEST_START;否則調用插件的handle_connection_close接口。??
  • 執行connection_close(srv,?con);和connection_reset(srv,?con);將連接關閉。??
  • CON_STATE_ERROR???/*?transient?*/??
  • 調用插件handle_request_done;??
  • 調用插件handle_connection_close;??
  • 執行connection_close將連接關閉。??
  • CON_STATE_CLOSE??
  • connection_close(srv,?con);將連接關閉。??

  • 以上是狀態機的概況。

    總覽了狀態機,我們知道狀態機會針對相應的階段對事件進行處理,那么狀態機是如何處理這些事件的?

    事實上,對于事件的處理,一部分是由lighttpd完成的,而一部分是由插件完成的。插件中那些負責事件處理的接口分布在某幾個狀態中。我們只需在插件的各個階段完成指定工作并返回相應的返回值,就可以促使狀態機完成狀態切換,完成事件的整套處理流程,并最終由lighttpd完成事件的響應。

    在插件中,我們可以編寫代碼來注冊lighttpd提供的回調接口,lighttpd在初始化階段、狀態機執行階段、退出階段會分別調用這些回調函數,完成插件的實例化,初始化,連接重置,事件處理,插件釋放等功能。

    要了解lighttpd對插件的調用方式,需要明白一個概念:事件接管。

    對于每個事件,都有一個mode字段(con->mode)。該字段的定義:

    typedef enum { DIRECT, EXTERNAL } connection_type;

    連接對象有一個字段mode用來標識該連接是最初由服務器accept產生的客戶端連接還是插件產生的其他輔助連接,當mode=DIRECT時表示對應連接由lighttpd服務器accept產生,mode!=DIRECT時表示對應連接是由插件產生的。

    事件(con)初始化時mode是DIRECT;connection_reset(srv,con);

    lighttpd在大部分流程中會在入口檢查到mode != DIRECT時直接返回GO_ON。即:此事件由用戶插件接管,lighttpd不參與。

    用戶編寫的插件應通過將mode置為插件自身的ID達到接管的作用。插件ID是在插件加載時由插件的加載順序確定的,是插件的唯一標識。

    用戶編寫插件在每個接口的一開始應該判斷mode是否等于自身的ID,若相等才能繼續執行,否則直接退出,返回GO_ON。

    了解了以上概念之后,我們就可以理解lighttpd對插件的調用方式了:

    在lighttpd需要調用插件某一個階段的接口函數時,會對所有插件注冊在該處的接口順序調用,順序與插件加載順序相同。例如:調用uri_raw接口,會先調用A插件的mod_A_uri_raw,然后調用B插件的mod_B_uri_raw,直到將所有已加載插件這個位置的接口全部調用完成。但實際處理這次事件通常只有一個插件,即插件ID與mode相同的那個插件。

    因此,假設在CON_STATE_HANDLE_REQUEST狀態,lighttpd調用了插件的handle_uri_raw接口,但是我們有多個插件,每個插件都注冊了handle_uri_raw這個接口,lighttpd也能辨別出要使用哪個插件。

    如果插件在處理事件的過程中,想讓lighttpd接管,還需要把mode置為DIRECT才行。

    以上是lighttpd狀態機和插件的總覽概況。

    總結

    以上是生活随笔為你收集整理的Lighttpd源码分析之状态机与插件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。