【PHP核心學習】深入理解FastCGI

來源:互聯網
上載者:User

標籤:blog   class   code   tar   ext   int   

|=-----------------------------------------------------------------------=||=------------------------=[ 深入理解FastCGI ]=--------------------------=||=-----------------------------------------------------------------------=||=--------------------------=[  by d4shman  ]=---------------------------=||=-----------------------------------------------------------------------=||=-------------------------=[  May 7, 2014  ]=---------------------------=||=-----------------------------------------------------------------------=|[目錄]0x01 什麼是FastCGI0x02 FastCGI的工作流程0x03 PHP中的CGI實現0x04 參考文獻0x01 什麼是FastCGI    CGI全稱是“通用閘道介面”( Common Gateway Interface),它可以讓一個用戶端從網頁瀏覽器向執行在web伺服器上的程式請求資料。CGI描述了用戶端和這個程式之間傳遞資料的一種標準。    FastCGI是web伺服器和處理常式之間通訊的一種協議, 是CGI的一種改進方案, FastCGI像是一個常駐(long live)型的CGI, 它可以一直執行,在請求到達時不會花費時間去fork一個進程來處理(這是CGI最為人詬病的fork-and-execute模式)。    CGI程式反覆載入是CGI效能低下的主要原因,FastCGI將CGI解譯器進程保持在記憶體內中,以此獲得較高的效能。同時,FastCGI還支援分散式運算,也就是說,Web Server和FastCGI可以部署在不同的伺服器上。0x02 FastCGI的工作流程    1.Web server啟動時載入FastCGI進程管理器(Apache Module、IIS ISAPI等)    2.FastCGI進程管理器自身初始化,啟動多個CGI解譯器進程php-cgi並等待來自      Web Server的串連。    3.當用戶端的請求到達Web Server時,FastCGI選擇並串連到一個CGI解譯器。      Web server將CGI環境變數和標準輸入發送到FastCGI子進程php-cgi。    4.FastCGI子進程完成處理後將標準輸出和錯誤資訊從同一串連返回Web Server。      php-cgi關閉本次串連並等待下次串連。  0x03 PHP中的CGI實現    PHP中的CGI實現了FastCGI協議,是一個TCP或UDP協議的伺服器接受來自Web伺服器的請求,當啟動時建立TCP/UDP協議的伺服器socket監聽,並接受相關請求進行處理。隨後就進入了PHP的生命週期:模組初始化,sapi初始化,處理PHP請求,模組關閉,sapi關閉。以上構成了PHP中CGI的生命週期。    以TCP為例,在TCP的服務端,一般會執行這樣的幾個操作步驟:        1.調用socket函數建立一個TCP用的流式通訊端    2.調用bind函數將伺服器的本地地址與前面建立的通訊端綁定    3.調用listen函數監聽新建立的通訊端,等待用戶端發起的串連請求    4.伺服器處理序調用accept函數進入阻塞狀態,知道有客戶進程調用connect函數建      立串連    5.當串連建立後,伺服器調用read_stream函數讀取用戶端的請求    6.處理完資料後,伺服器調用write函數向用戶端發送應答        <!-------------- 這就是活生生的socket通訊啊 --------------->         下面從PHP源碼來看這個過程:    (以下代碼我只保留了關鍵區段,完整代碼請自行查看PHP源碼)    1.socket的建立、綁定和監聽(在源碼的sapi/cgi/fastcgi.c中)    /* Create, bind socket and start listen on it */    if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||    #ifdef SO_REUSEADDR        setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0 ||    #endif        bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||        listen(listen_socket, backlog) < 0) {            fprintf(stderr, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno));        return -1;    }    2.當服務端初始化完成後,進程調用accept函數進入阻塞狀態,在main函數中我們看到如下代碼:    while (parent) {        do {            pid = fork();   //  fork出新的子進程            switch (pid) {            case 0:                 parent = 0;                    /* don‘t catch our signals */                sigaction(SIGTERM, &old_term, 0);   //  終止訊號                sigaction(SIGQUIT, &old_quit, 0);   //  終端退出符                sigaction(SIGINT,  &old_int,  0);   //  終端中斷符                break;                ...                default:                /* Fine */                running++;                break;        } while (parent && (running < children));    /* 調用fcgi_accept_request接受請求 */        while (!fastcgi || fcgi_accept_request(&request) >= 0) {    SG(server_context) = (void *) &request;    init_request_info(TSRMLS_C);    CG(interactive) = 0;        }    }    3.調用read函數讀取用戶端請求:    static int fcgi_read_request(fcgi_request *req)    {        fcgi_header hdr;        int len, padding;        unsigned char buf[FCGI_MAX_LENGTH+8];                req->keep = 0;        req->closed = 0;        req->in_len = 0;        req->out_hdr = NULL;        req->out_pos = req->out_buf;        req->has_env = 1;        /*調用sage_read讀取fcgi_request類型的資料req*/        if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||            hdr.version < FCGI_VERSION_1) {            return 0;        }    }        在請求初始化完成,讀取請求完畢後,就該處理請求的PHP檔案了。 假設此次請求為PHP_MODE_STANDARD則會調用php_execute_script執行PHP檔案。在此函數中它先初始化此檔案相關的一些內容,然後再調用zend_execute_scripts函數,對PHP檔案進行詞法分析和文法分析,產生中間代碼, 並執行zend_execute函數,從而執行這些中間代碼。    4.fastCGI處理完成    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;    }    如上,當socket處於開啟狀態(reg->fd >= 0),並且請求未關閉,則會將執行後的結果刷到用戶端,並將請求的關閉設定為真。 將資料刷到用戶端的程式調用的是fcgi_flush函數。在此函數中,關鍵是在於答應頭的構造和寫操作。 程式的寫操作是調用的safe_write函數,而safe_write函數中對於最終的寫操作針對win和linux環境做了區分,在Win32下,如果是TCP串連則用send函數,如果是非TCP則和非win環境一樣使用write函數。如下代碼:        static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)    {        int    ret;        size_t n = 0;            do {            errno = 0;        #ifdef _WIN32 /*win32環境*/            if (!req->tcp) { /*非TCP串連,調用write函數*/                ret = write(req->fd, ((char*)buf)+n, count-n);            } else {         /*TCP串連,調用send函數*/                ret = send(req->fd, ((char*)buf)+n, count-n, 0);                if (ret <= 0) {                    errno = WSAGetLastError();                }            }        #else  /*其他環境, 調用write函數*/            ret = write(req->fd, ((char*)buf)+n, count-n);        #endif            if (ret > 0) {                n += ret;            } else if (ret <= 0 && errno != 0 && errno != EINTR) {                return ret;            }        } while (n != count);        return n;    }    以上就是基於TCP串連的PHP FastCGI的實現過程。0x04 參考文獻《深入理解PHP核心》    

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.