from: http://www.csdn.net/article/2014-09-26/2821885-exploring-of-the-php-2
摘要:PHP作為一門簡單而強大的語言,能夠提供很多Web適用的語言特性。從實踐出發,繼弱類型變數原理探究後,王帥將繼續帶大家弄清PHP核心中的一些常用部分,本期則是SAPI的深入理解。
SAPI是Server Application Programming Interface(伺服器應用編程介面)的縮寫。PHP通過SAPI提供了一組介面,供應用和PHP核心之間進行資料互動。
簡單的講,就像函數的輸入和輸出一樣,我們通過Linux命令列執行一段PHP代碼,本質是Linux的Shell通過PHP的SAPI傳入一組參數,Zend引擎執行後,返回給shell,由shell顯示出來的過程。同樣的,通過Apache調用PHP,通過Web伺服器給SAPI傳入資料,Zend引擎執行後,返回給Apache,由Apache顯示在頁面上。
圖1. PHP架構圖
PHP提供很多種形式的介面,包括apache、apache2filter、apache2handler、caudium、cgi 、cgi-fcgi、cli、cli-server、continuity、embed、isapi、litespeed、milter、nsapi、phttpd pi3web、roxen、thttpd、tux和webjames。但是常用的只有5種形式,CLI/CGI(命令列)、Multiprocess(多進程)、Multithreaded(多線程)、FastCGI和Embedded(內嵌)。
PHP提供了一個函數查看當前SAPI介面類型:
[php] view plain copy string php_sapi_name ( void ) PHP的運行和載入
無論使用哪種SAPI,在PHP執行指令碼前後,都包含一系列事件:Module的Init(MINT)和Shutdown(MSHUTDOWN),Request 的Init(RINT)和Shutdown(RSHUTDOWN)。 第一階段是PHP模組初始化階段(MINT),可以初始化擴充內部變數、分配資源和註冊資源處理器,在整個PHP執行個體生命週期內,該過程只執行一次。
什麼是PHP模組。通過上面的PHP架構圖,在PHP中可以使用get_loaded_extensions 函數來查看所有編譯並載入的模組/擴充,相當於CLI模式下的php -m。
以PHP的Memcached擴充原始碼為例:
[php] view plain copy PHP_MINIT_FUNCTION(memcached) { zend_class_entry ce; memcpy(&memcached_object_handlers,zend_get_std_object_handlers(), sizeof(zend_object_handlers)); memcached_object_handlers.clone_obj = NULL; /* 執行了一些類似的初始化操作 */ return SUCCESS; } 第二階段是請求初始化階段(RINT),在模組初始化並啟用後,會建立PHP運行環境,同時調用所有模組註冊的RINT函數,調用每個擴充的請求初始化函數 ,設定特定的環境變數、分配資源或執行其他任務,如審核。
[php] view plain copy PHP_RINIT_FUNCTION(memcached) { /* 執行一些關於請求的初始化 */ return SUCCESS; }
第三階段,請求處理完成後,會調用PHP_RSHUTDOWN_FUNCTION進行回收,這是每個擴充的請求關閉函數,執行最後的清理工作。Zend引擎執行清理過程、垃圾收集、對之前的請求期間用到的每個變數執行unset。請求完成可能是執行到指令碼完成,也可能是調用die()或exit()函數完成
第四階段,當PHP生命週期結束時候,PHP_MSHUTDOWN_FUNCTION對模組進行回收處理,這是每個擴充的模組關閉函數,用於關閉自己的核心子系統。
[php] view plain copy PHP_MSHUTDOWN_FUNCTION(memcached) { /* 執行關於模組的銷毀工作 */ UNREGISTER_INI_ENTRIES(); return SUCCESS; }
常見的運行模式
常見的SAPI模式有五種:
CLI和CGI模式(單進程模式) 多進程模式 多線程模式 FastCGI模式 嵌入式
1. CLI/CGI模式
CLI和CGI都屬於單進程模式,PHP的生命週期在一次請求中完成。也就是說每次執行PHP指令碼,都會執行第二部分講的四個INT和Shutdown事件。
圖2. CGI/CLI生命週期
2. 多進程模式(Multiprocess)
多進程模式可以將PHP內建到Web Server中,PHP可以編譯成Apache下的prefork MPM模式和APXS模組,當Apache啟動後,會fork很多子進程,每個子進程擁有自己獨立的進程地址空間。
圖3. 多進程模式生命週期
在一個子進程中,PHP的生命週期是調用MINT啟動後,執行多次請求(RINT/RSHUTDOWN),在Apache關閉或進程結束後,才會調用MSHUTDOWN進行回收階段。
圖4. 多進程的生命週期
多進程模型中,每個子進程都是獨立運行,沒有代碼和資料共用,因此一個子進程終止退出和重建,不會影響其他子進程的穩定。
3. 多線程模式(Multithreaded)
Apache2的Worker MPM採用了多執行緒模式,在一個進程下建立多個線程,在同一個進程地址空間執行。
圖5. 多線程生命週期
4. FastCGI模式
在我們用的Nginx+PHP-FPM用的就是FastCGI模式,Fastcgi是一種特殊的CGI模式,是一種常駐進程類型的CGI,運行後可以Fork多個進程,不用花費時間動態Fork子進程,也不需要每次請求都調用MINT/MSHUTDOWN。PHP通過PHP-FPM來管理和調度FastCGI的進程池。Nginx和PHP-FPM通過本地的TCP Socket和Unix Socket 進行通訊。
圖6. FastCGI模式生命週期
PHP-FPM進程管理器自身初始化,啟動多個CGI解譯器進程等待來自Nginx的請求。當用戶端請求達到PHP-FPM,管理器選擇到一個CGI進程進行處理,Nginx將CGI環境變數和標準輸入發送到一個PHP-CIG子進程。PHP-CGI子進程處理完成後,將標準輸出和錯誤資訊返回給Nginx,當PHP-CGI子進程關閉串連時,請求處理完成。PHP-CGI子進程等待著下一個串連。
可以想象CGI的系統開銷有多大。每一個Web 請求PHP都必須重新解析php.ini、載入全部擴充並始化全部資料結構。使用FastCGI,所有這些都只在進程啟動時發生一次。另外,對於資料庫和Memcache的持續串連可以工作。
5. 內嵌模式(Embedded)
Embed SAPI是一種特殊的SAPI,允許在C/C++語言中調用PHP提供的函數。這種SAPI和CLI模式一樣,按照Module Init => Request Init => Request => Request Shutdown => Module Shutdown的模式運行。
Embed SAPI可以調用PHP豐富的類庫,也可以實現進階玩法,比如可以查看PHP的OPCODE(PHP執行的中間碼,Zend引擎的指令,由PHP代碼產生)。
詳細請見: http://www.laruence.com/2008/09/23/539.html SAPI的運行機制
我們以CGI為例,看一下SAPI的運行機制。
[php] view plain copy