PHP SAPI(FCGI)個人整理

2021-08-01 16:19:31 字數 3185 閱讀 4092

php的cgi實現從cgi_main.c檔案的main函式開始,在main函式中呼叫了定義在fastcgi.c檔案中的初始化,監聽等函式。對比tcp的流程,我們檢視php對tcp協議的實現,雖然php本身也實現了這些流程,但是在main函式中一些過程被封裝成乙個函式實現。對應tcp的操作流程,php首先會執行建立socket,繫結套接字,建立監聽:

if (bindpath)
在fastcgi.c檔案中,fcgi_listen函式主要用於建立、繫結socket並開始監聽,它走完了前面所列tcp流程的前三個階段,

if ((listen_socket = socket(sa.sa.sa_family, sock_stream, 0)) < 0 ||

...bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||

listen(listen_socket, backlog) < 0)

當服務端初始化完成後,程序呼叫accept函式進入阻塞狀態,在main函式中我們看到如下**:

while (parent)  while (parent && (running < children));

...while (!fastcgi || fcgi_accept_request(&request) >= 0)

如上的**是乙個生成子程序,並等待使用者請求。在fcgi_accept_request函式中,程式會呼叫accept函式阻塞新建立的程序。當使用者的請求到達時,fcgi_accept_request函式會判斷是否處理使用者的請求,其中會過濾某些連線請求,忽略受限制客戶的請求,如果程式受理使用者的請求,它將分析請求的資訊,將相關的變數寫到對應的變數中。其中在讀取請求內容時呼叫了safe_read方法。如下所示:[main() -> fcgi_accept_request() -> fcgi_read_request() -> safe_read()]

static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count) while (n != count);

}

如上對應伺服器端讀取使用者的請求資料。

在請求初始化完成,讀取請求完畢後,就該處理請求的php檔案了。假設此次請求為php_mode_standard則會呼叫php_execute_script執行php檔案。在此函式中它先初始化此檔案相關的一些內容,然後再呼叫zend_execute_scripts函式,對php檔案進行詞法分析和語法分析,生成中間**,並執行zend_execute函式,從而執行這些中間**。關於整個指令碼的執行請參見第三節 指令碼的執行。

在處理完使用者的請求後,伺服器端將返回資訊給客戶端,此時在main函式中呼叫的是fcgi_finish_request(&request, 1); fcgi_finish_request函式定義在fastcgi.c檔案中,其**如下:

int fcgi_finish_request(fcgi_request *req, int force_close)

fcgi_close(req, force_close, 1);}return ret;}

如上,當socket處於開啟狀態,並且請求未關閉,則會將執行後的結果刷到客戶端,並將請求的關閉設定為真。將資料刷到客戶端的程式呼叫的是fcgi_flush函式。在此函式中,關鍵是在於答應頭的構造和寫操作。程式的寫操作是呼叫的safe_write函式,而safe_write函式中對於最終的寫操作針對win和linux環境做了區分,在win32下,如果是tcp連線則用send函式,如果是非tcp則和非win環境一樣使用write函式。如下**:

#ifdef _win32if (!req->tcp)  else }#else

ret = write(req->fd, ((char*)buf)+n, count-n);#endif

在傳送了請求的應答後,伺服器端將會執行關閉操作,僅限於cgi本身的關閉,程式執行的是fcgi_close函式。 fcgi_close函式在前面提的fcgi_finish_request函式中,在請求應答完後執行。同樣,對於win平台和非win平台有不同的處理。其中對於非win平台呼叫的是write函式。

以上是乙個tcp伺服器端實現的簡單說明。這只是我們php的cgi模式的基礎,在這個基礎上php增加了更多的功能。在前面的章節中我們提到了每個sapi都有乙個專屬於它們自己的sapi_module_struct結構:cgi_sapi_module,其**定義如下:

/* ;/* }}} */
同樣,以讀取cookie為例,當我們在cgi環境下,在php中呼叫讀取cookie時,最終獲取的資料的位置是在啟用sapi時。它所呼叫的方法是read_cookies。由sapi實現來實現獲取cookie,這樣各個不同的sapi就能根據自己的需要來實現一些依賴環境的方法。

sg(request_info).cookie_data = sapi_module.read_cookies(tsrmls_c);

所有使用php的場合都需要定義自己的sapi,例如在第一小節的apache模組方式中, sapi_module是apache2_sapi_module,其對應read_cookies方法的是php_apache_sapi_read_cookies函式,而在我們這裡,讀取cookie的函式是sapi_cgi_read_cookies。從sapi_module結構可以看出flush對應的是sapi_cli_flush,在win或非win下,flush對應的操作不同,在win下,如果輸出快取失敗,則會和嵌入式的處理一樣,呼叫php_handle_aborted_connection進入中斷處理程式,而其它情況則是沒有任何處理程式。這個區別通過cli_win.c中的php_cli_win32_no_console控制。

個人總結:php fcgi的實現與cgi的區別就是,

1、fcgi是通過scoket監聽來獲取請求的指令碼檔案和引數,cgi是通過環境變數來獲取,

2、因為fcgi通過socket來獲取需要執行的資訊,因為它可以迴圈在處理過程中而不用每次請求都重新啟動乙個程序,重新初始化各個模組,初始化很多的全域性變數,讀取php.ini設定等

3、fcgi可以同時啟動多個,多個程序之前監聽同乙個socket埠,但是他們之前會有乙個競爭鎖來防止驚群現象的發生。

4、php-fpm是用來管理多個fcgi程序的,比如保持程序的數量,增加程序的數量,重啟已經處理了很多請求的程序等等。

redux 個人整理

閱讀了redux的文件後,並參與了乙個練手專案r music的部分開發後,通過自己的理解,整理了這篇文件。初學,所以一定會有不詳或者錯誤的地方。開發這個專案,我參閱的學習文件如下 react redux提供的provider元件,可以讓容器元件取得state。src index.js import ...

JQ個人整理

jq選擇器 基礎選擇器 標籤名 類名 id名 層疊選擇器 a b a中所有的子孫b a b a中兒子b a b a緊跟在後面的兄弟 a b a後面所有的兄弟b 過濾選擇器 first 第乙個 last 最後乙個 not 非 even 偶數 odd 奇數 eq 2 等於 多種過濾器組合時,在滿足第乙個...

mybatis個人整理

三層架構概念 介面層,業務邏輯層,資料訪問層 三層架構對應的包 介面層 controller包 servlet 業務邏輯層 service包 service 資料訪問層 dao包 dao 三層架構對應的框架 介面層 servlet springmvc框架 業務邏輯層 service類 spring框...