久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

PHP的CGI實(shí)現(xiàn) | PHP源碼閱讀,PHP設(shè)計(jì)模式

 corefashion 2014-08-22

FastCGI簡介

CGI全稱是“通用網(wǎng)關(guān)接口”(Common Gateway Interface),, 它可以讓一個(gè)客戶端,,從網(wǎng)頁瀏覽器向執(zhí)行在Web服務(wù)器上的程序請(qǐng)求數(shù)據(jù)。 CGI描述了客戶端和這個(gè)程序之間傳輸數(shù)據(jù)的一種標(biāo)準(zhǔn),。 CGI的一個(gè)目的是要獨(dú)立于任何語言的,,所以CGI可以用任何一種語言編寫,只要這種語言具有標(biāo)準(zhǔn)輸入,、輸出和環(huán)境變量,。如php,perl,tcl等

FastCGI像是一個(gè)常駐(long-live)型的CGI, 它可以一直執(zhí)行著,,只要激活后,,不會(huì)每次都要花費(fèi)時(shí)間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)。 它還支持分布式的運(yùn)算, 即 FastCGI 程序可以在網(wǎng)站服務(wù)器以外的主機(jī)上執(zhí)行并且接受來自其它網(wǎng)站服務(wù)器來的請(qǐng)求,。

FastCGI是語言無關(guān)的,、可伸縮架構(gòu)的CGI開放擴(kuò)展,其主要行為是將CGI解釋器進(jìn)程保持在內(nèi)存中并因此獲得較高的性能,。 眾所周知,,CGI解釋器的反復(fù)加載是CGI性能低下的主要原因,如果CGI解釋器保持在內(nèi)存中并接受FastCGI進(jìn)程管理器調(diào)度,, 則可以提供良好的性能,、伸縮性、Fail- Over特性等等,。

一般情況下,,F(xiàn)astCGI的整個(gè)工作流程是這樣的。

  1. Web Server啟動(dòng)時(shí)載入FastCGI進(jìn)程管理器(IIS ISAPI或Apache Module)
  2. FastCGI進(jìn)程管理器自身初始化,,啟動(dòng)多個(gè)CGI解釋器進(jìn)程(可見多個(gè)php-cgi)并等待來自Web Server的連接,。
  3. 當(dāng)客戶端請(qǐng)求到達(dá)Web Server時(shí),F(xiàn)astCGI進(jìn)程管理器選擇并連接到一個(gè)CGI解釋器,。Web server將CGI環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進(jìn)程php-cgi,。
  4. FastCGI子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回Web Server。當(dāng)FastCGI子進(jìn)程關(guān)閉連接時(shí),,請(qǐng)求便告處理完成,。FastCGI子進(jìn)程接著等待并處理來自FastCGI進(jìn)程管理器(運(yùn)行在Web Server中)的下一個(gè)連接,。 在CGI模式中,php-cgi在此便退出了,。

PHP中的CGI實(shí)現(xiàn)

PHP的CGI實(shí)現(xiàn)本質(zhì)是是以socket編程實(shí)現(xiàn)一個(gè)TCP或UDP協(xié)議的服務(wù)器,當(dāng)啟動(dòng)時(shí),,創(chuàng)建TCP/UDP協(xié)議的服務(wù)器的socket監(jiān)聽,, 并接收相關(guān)請(qǐng)求進(jìn)行處理。這只是請(qǐng)求的處理,,在此基礎(chǔ)上添加模塊初始化,,sapi初始化,模塊關(guān)閉,,sapi關(guān)閉等就構(gòu)成了整個(gè)CGI的生命周期,。

以TCP為例,在TCP的服務(wù)端,,一般會(huì)執(zhí)行這樣幾個(gè)操作步驟:

  1. 調(diào)用socket函數(shù)創(chuàng)建一個(gè)TCP用的流式套接字,;
  2. 調(diào)用bind函數(shù)將服務(wù)器的本地地址與前面創(chuàng)建的套接字綁定;
  3. 調(diào)用listen函數(shù)將新創(chuàng)建的套接字作為監(jiān)聽,,等待客戶端發(fā)起的連接,,當(dāng)客戶端有多個(gè)連接連接到這個(gè)套接字時(shí),可能需要排隊(duì)處理,;
  4. 服務(wù)器進(jìn)程調(diào)用accept函數(shù)進(jìn)入阻塞狀態(tài),,直到有客戶進(jìn)程調(diào)用connect函數(shù)而建立起一個(gè)連接;
  5. 當(dāng)與客戶端創(chuàng)建連接后,,服務(wù)器調(diào)用read_stream函數(shù)讀取客戶的請(qǐng)求,;
  6. 處理完數(shù)據(jù)后,服務(wù)器調(diào)用write函數(shù)向客戶端發(fā)送應(yīng)答,。

TCP上客戶-服務(wù)器事務(wù)的時(shí)序如圖2.6所示:

TCP上客戶-服務(wù)器事務(wù)的時(shí)序

PHP的CGI實(shí)現(xiàn)從cgi_main.c文件的main函數(shù)開始,,在main函數(shù)中調(diào)用了定義在fastcgi.c文件中的初始化,監(jiān)聽等函數(shù),。 對(duì)比TCP的流程,,我們查看PHP對(duì)TCP協(xié)議的實(shí)現(xiàn),雖然PHP本身也實(shí)現(xiàn)了這些流程,,但是在main函數(shù)中一些過程被封裝成一個(gè)函數(shù)實(shí)現(xiàn),。 對(duì)應(yīng)TCP的操作流程,PHP首先會(huì)執(zhí)行創(chuàng)建socket,綁定套接字,,創(chuàng)建監(jiān)聽:

if (bindpath) {
    fcgi_fd = fcgi_listen(bindpath, 128);   //  實(shí)現(xiàn)socket監(jiān)聽,,調(diào)用fcgi_init初始化
    ...
}

在fastcgi.c文件中,fcgi_listen函數(shù)主要用于創(chuàng)建,、綁定socket并開始監(jiān)聽,,它走完了前面所列TCP流程的前三個(gè)階段,,

    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) {
        ...
    }

當(dāng)服務(wù)端初始化完成后,進(jìn)程調(diào)用accept函數(shù)進(jìn)入阻塞狀態(tài),,在main函數(shù)中我們看到如下代碼:

    while (parent) {
        do {
            pid = fork();   //  生成新的子進(jìn)程
            switch (pid) {
            case 0: //  子進(jìn)程
                parent = 0;

                /* don't catch our signals */
                sigaction(SIGTERM, &old_term, 0);   //  終止信號(hào)
                sigaction(SIGQUIT, &old_quit, 0);   //  終端退出符
                sigaction(SIGINT,  &old_int,  0);   //  終端中斷符
                break;
                ...
                default:
                /* Fine */
                running++;
                break;
        } while (parent && (running < children));

    ...
        while (!fastcgi || fcgi_accept_request(&request) >= 0) {
        SG(server_context) = (void *) &request;
        init_request_info(TSRMLS_C);
        CG(interactive) = 0;
                    ...
            }

如上的代碼是一個(gè)生成子進(jìn)程,,并等待用戶請(qǐng)求。在fcgi_accept_request函數(shù)中,,程序會(huì)調(diào)用accept函數(shù)阻塞新創(chuàng)建的進(jìn)程,。 當(dāng)用戶的請(qǐng)求到達(dá)時(shí),fcgi_accept_request函數(shù)會(huì)判斷是否處理用戶的請(qǐng)求,,其中會(huì)過濾某些連接請(qǐng)求,,忽略受限制客戶的請(qǐng)求, 如果程序受理用戶的請(qǐng)求,,它將分析請(qǐng)求的信息,,將相關(guān)的變量寫到對(duì)應(yīng)的變量中。 其中在讀取請(qǐng)求內(nèi)容時(shí)調(diào)用了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)
{
    size_t n = 0;
    do {
    ... //  省略  對(duì)win32的處理
        ret = read(req->fd, ((char*)buf)+n, count-n);   //  非win版本的讀操作
    ... //  省略
    } while (n != count);

}

如上對(duì)應(yīng)服務(wù)器端讀取用戶的請(qǐng)求數(shù)據(jù),。

在請(qǐng)求初始化完成,讀取請(qǐng)求完畢后,,就該處理請(qǐng)求的PHP文件了,。 假設(shè)此次請(qǐng)求為PHP_MODE_STANDARD則會(huì)調(diào)用php_execute_script執(zhí)行PHP文件。 在此函數(shù)中它先初始化此文件相關(guān)的一些內(nèi)容,,然后再調(diào)用zend_execute_scripts函數(shù),,對(duì)PHP文件進(jìn)行詞法分析和語法分析,生成中間代碼,, 并執(zhí)行zend_execute函數(shù),,從而執(zhí)行這些中間代碼。關(guān)于整個(gè)腳本的執(zhí)行請(qǐng)參見第三節(jié) 腳本的執(zhí)行,。

在處理完用戶的請(qǐng)求后,,服務(wù)器端將返回信息給客戶端,此時(shí)在main函數(shù)中調(diào)用的是fcgi_finish_request(&request, 1); fcgi_finish_request函數(shù)定義在fastcgi.c文件中,,其代碼如下:

int fcgi_finish_request(fcgi_request *req, int force_close)
{
int ret = 1;

if (req->fd >= 0) {
    if (!req->closed) {
        ret = fcgi_flush(req, 1);
        req->closed = 1;
    }
    fcgi_close(req, force_close, 1);
}
return ret;
}

如上,,當(dāng)socket處于打開狀態(tài),并且請(qǐng)求未關(guān)閉,,則會(huì)將執(zhí)行后的結(jié)果刷到客戶端,,并將請(qǐng)求的關(guān)閉設(shè)置為真。 將數(shù)據(jù)刷到客戶端的程序調(diào)用的是fcgi_flush函數(shù),。在此函數(shù)中,,關(guān)鍵是在于答應(yīng)頭的構(gòu)造和寫操作。 程序的寫操作是調(diào)用的safe_write函數(shù),,而safe_write函數(shù)中對(duì)于最終的寫操作針對(duì)win和linux環(huán)境做了區(qū)分,, 在Win32下,,如果是TCP連接則用send函數(shù),如果是非TCP則和非win環(huán)境一樣使用write函數(shù),。如下代碼:

#ifdef _WIN32
if (!req->tcp) {
    ret = write(req->fd, ((char*)buf)+n, count-n);
} else {
    ret = send(req->fd, ((char*)buf)+n, count-n, 0);
    if (ret <= 0) {
            errno = WSAGetLastError();
    }
}
#else
ret = write(req->fd, ((char*)buf)+n, count-n);
#endif

在發(fā)送了請(qǐng)求的應(yīng)答后,,服務(wù)器端將會(huì)執(zhí)行關(guān)閉操作,僅限于CGI本身的關(guān)閉,,程序執(zhí)行的是fcgi_close函數(shù),。 fcgi_close函數(shù)在前面提的fcgi_finish_request函數(shù)中,在請(qǐng)求應(yīng)答完后執(zhí)行,。同樣,對(duì)于win平臺(tái)和非win平臺(tái)有不同的處理,。 其中對(duì)于非win平臺(tái)調(diào)用的是write函數(shù),。

以上是一個(gè)TCP服務(wù)器端實(shí)現(xiàn)的簡單說明。這只是我們PHP的CGI模式的基礎(chǔ),,在這個(gè)基礎(chǔ)上PHP增加了更多的功能,。 在前面的章節(jié)中我們提到了每個(gè)SAPI都有一個(gè)專屬于它們自己的sapi_module_struct結(jié)構(gòu):cgi_sapi_module,其代碼定義如下:

/* {{{ sapi_module_struct cgi_sapi_module
 */
static sapi_module_struct cgi_sapi_module = {
"cgi-fcgi",                     /* name */
"CGI/FastCGI",                  /* pretty name */

php_cgi_startup,                /* startup */
php_module_shutdown_wrapper,    /* shutdown */

sapi_cgi_activate,              /* activate */
sapi_cgi_deactivate,            /* deactivate */

sapi_cgibin_ub_write,           /* unbuffered write */
sapi_cgibin_flush,              /* flush */
NULL,                           /* get uid */
sapi_cgibin_getenv,             /* getenv */

php_error,                      /* error handler */

NULL,                           /* header handler */
sapi_cgi_send_headers,          /* send headers handler */
NULL,                           /* send header handler */

sapi_cgi_read_post,             /* read POST data */
sapi_cgi_read_cookies,          /* read Cookies */

sapi_cgi_register_variables,    /* register server variables */
sapi_cgi_log_message,           /* Log message */
NULL,                           /* Get request time */
NULL,                           /* Child terminate */

STANDARD_SAPI_MODULE_PROPERTIES
};
/* }}} */

同樣,,以讀取cookie為例,,當(dāng)我們?cè)贑GI環(huán)境下,在PHP中調(diào)用讀取Cookie時(shí),, 最終獲取的數(shù)據(jù)的位置是在激活SAPI時(shí),。它所調(diào)用的方法是read_cookies。

SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C);

對(duì)于每一個(gè)服務(wù)器在加載時(shí),,我們都指定了sapi_module,,在第一小節(jié)的Apache模塊方式中, sapi_module是apache2_sapi_module,,其對(duì)應(yīng)read_cookies方法的是php_apache_sapi_read_cookies函數(shù),, 而在我們這里,讀取cookie的函數(shù)是sapi_cgi_read_cookies,。 再次說明定義SAPI結(jié)構(gòu)的理由:統(tǒng)一接口,,面向接口的編程,具有更好的擴(kuò)展性和適應(yīng)性,。

參考資料

  • http://www./drupal/node/2
  • http://baike.baidu.com/view/641394.htm

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多