PHP中的CGI實現

2021-10-05 11:23:06 字數 3679 閱讀 1086

php的cgi實現本質是是以socket程式設計實現乙個tcp或udp協議的伺服器,當啟動時,建立tcp/udp協議的伺服器的socket監聽, 並接收相關請求進行處理。這只是請求的處理,在此基礎上新增模組初始化,sapi初始化,模組關閉,sapi關閉等就構成了整個cgi的生命週期。

以tcp為例,在tcp的服務端,一般會執行這樣幾個操作步驟:

呼叫socket函式建立乙個tcp用的流式套接字

呼叫bind函式將伺服器的本地位址與前面建立的套接字繫結

呼叫listen函式將新建立的套接字作為監聽,等待客戶端發起的連線,當客戶端有多個連線連線到這個套接字時,可能需要排隊處理

伺服器程序呼叫accept函式進入阻塞狀態,直到有客戶程序呼叫connect函式而建立起乙個連線

當與客戶端建立連線後,伺服器呼叫read_stream函式讀取客戶的請求

處理完資料後,伺服器呼叫write函式向客戶端傳送應答

tcp上客戶-伺服器事務的時序如圖2.6所示:

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。

sg

(request_info)

.cookie_data = sapi_module.

read_cookies

(tsrmls_c)

;

對於每乙個伺服器在載入時,我們都指定了sapi_module,在第一小節的apache模組方式中, sapi_module是apache2_sapi_module,其對應read_cookies方法的是php_apache_sapi_read_cookies函式, 而在我們這裡,讀取cookie的函式是sapi_cgi_read_cookies。 再次說明定義sapi結構的理由:統一介面,面向介面的程式設計,具有更好的擴充套件性和適應

談談在WebServer中如何實現CGI技術

談談在webserver中如何實現cgi技術 在webserver中,cgi技術的實現相信許多人很感興趣,不過在一些開源軟體如apache中,由於軟體規模大,相關模組多,直接去讀懂是如何實現的比較費勁,下面就來談談cgi技術的實現方法。要實現cgi技術,關鍵是要實現執行其他應用程式時,將應用程式的輸...

談談在WebServer中如何實現CGI技術

談談在webserver中如何實現cgi技術 在webserver中,cgi技術的實現相信許多人很感興趣,不過在一些開源軟體如apache中,由於軟體規模大,相關模組多,直接去讀懂是如何實現的比較費勁,下面就來談談cgi技術的實現方法。要實現cgi技術,關鍵是要實現執行其他應用程式時,將應用程式的輸...

談談在WebServer中如何實現CGI技術

談談在webserver中如何實現cgi技術 在webserver中,cgi技術的實現相信許多人很感興趣,不過在一些開源軟體如apache中,由於軟體規模大,相關模組多,直接去讀懂是如何實現的比較費勁,下面就來談談cgi技術的實現方法。要實現cgi技術,關鍵是要實現執行其他應用程式時,將應用程式的輸...