先看下PHP的結構圖:
1)Zend Engine
Zend引擎是PHP實作的核心,提供了語言實作上的基礎設施。例如:PHP的文法實作,腳本的編譯運作環境, 擴充機制以及記憶體管理等。
2)Extensions
圍繞着zend引擎,Extensions通過元件式的方式提供各種基礎服務,我們常見的各種内置函數(如MySQL系列)、标準庫等都是通過Extension來實作,使用者也可以根據需要實作自己的extension以達到功能擴充、性能優化等目的。
3)SAPI
SAPI(Server Application Programming Interface)指的是PHP具體應用的程式設計接口。就像PC一樣,無論安裝哪些作業系統,隻要滿足了PC的接口規範都可以在PC上正常運作。
4)架構思想
如果php是一輛車,那麼車的架構就是php本身,Zend是車的引擎(發動機),Ext下面的各種元件就是車的輪子,SAPI可以看做是公路,車可以跑在不同類型的公路上,而一次php程式的執行就是汽車跑在公路上。是以,我們需要:性能優異的引擎+合适的車輪+正确的跑道。
常見的子產品包括mod_auth(權限驗證)、mod_ssl(SSL和TLS支援) mod_rewrite(URL重寫)等。
下圖為Apache的邏輯構成以及與作業系統的關系:
1)Apache的mod_php5子產品
當PHP需要在Apache伺服器下運作時,可以用mod_php5子產品的形式內建。
此時mod_php5子產品的作用是接收Apache傳遞過來的PHP檔案請求,并處理這些請求, 然後将處理後的結果傳回給Apache。
2)Apache的運作過程
Apache的運作分為啟動階段和運作階段。
在運作階段,Apache主要工作是處理使用者的服務請求。
Apache對HTTP的請求可以分為連接配接、處理和斷開連接配接三個大的階段。同時也可以分為11個小的階段:
Post-Read-Request,URI Translation,Header Parsing,Access Control,Authentication,
Authorization,MIME Type Checking,FixUp,Response,Logging,CleanUp。
3)Apache Hook機制
子產品可以在Apache的任何一個處理階段中挂接(Hook)上自己的處理函數,進而參與Apache的請求處理過程。
1)CGI
比如現在請求的是“index.php”,根據配置檔案,Apache知道這個不是靜态檔案,需要去找PHP解析器來處理,那麼它會把這個請求簡單處理後交給PHP解析器。Apache會傳url、查詢字元串、POST資料、HTTP header等,而CGI就是規定要傳哪些資料、以什麼樣的格式傳遞給後方處理這個請求的協定。
當web伺服器收到“index.php”這個請求後,會啟動對應的CGI程式,這裡就是PHP的解析器。接下來PHP解析器會解析php.ini、載入全部擴充并初始化全部資料結構,然後處理請求,再以CGI規定的格式傳回處理後的結果,退出程序,web伺服器再把結果傳回給浏覽器。
2)FastCGI
CGI程式存在性能問題,每次請求都會重複“PHP解析器會解析php.ini、載入全部擴充并初始化全部資料結構”這些步驟。
FastCGI工作流程的通俗版本如下:
1. Fastcgi會先啟一個master,解析配置檔案,初始化執行環境,然後再啟動多個worker。
2. 當請求過來時,master會傳遞給一個worker,然後立即可以接受下一個請求。這樣就避免了重複的勞動,效率自然是高。
3. 當worker不夠用時,master可以根據配置預先啟動幾個worker等着。
4. 當空閑worker太多時,也會停掉一些,這樣就提高了性能,也節約了資源。
FastCGI工作流程的專業版本如下:
1. Web Server啟動時載入FastCGI程序管理器(IIS ISAPI或Apache Module)
2. FastCGI程序管理器自身初始化,啟動多個CGI解釋器程序(可見多個php-cgi)并等待來自Web Server的連接配接。
3. 當用戶端請求到達Web Server時,FastCGI程序管理器選擇并連接配接到一個CGI解釋器。Web server将CGI環境變量和标準輸入發送到FastCGI子程序php-cgi。
4. FastCGI子程序完成處理後将标準輸出和錯誤資訊從同一連接配接傳回Web Server。當FastCGI子程序關閉連接配接時, 請求便告處理完成。FastCGI子程序接着等待并處理來自FastCGI程序管理器(運作在Web Server中)的下一個連接配接。 在CGI模式中,php-cgi在此便退出了。
3)PHP-CGI
PHP-CGI是PHP自帶的FastCGI管理器。
PHP-CGI的不足:
1. php-cgi變更php.ini配置後需重新開機php-cgi才能讓新的php-ini生效,不可以平滑重新開機。
2. 直接殺死php-cgi程序,php就不能運作了。(PHP-FPM和Spawn-FCGI就沒有這個問題,守護程序會平滑從新生成新的子程序。)
4)PHP-FPM
PHP-FPM是一個PHP FastCGI管理器,是隻用于PHP的。
PHP-FPM其實是PHP源代碼的一個更新檔,旨在将FastCGI程序管理整合進PHP包中。必須将它patch到你的PHP源代碼中,在編譯安裝PHP後才可以使用。
下圖為SAPI的簡單示意圖:
SAPI(Server abstraction API),它提供了一個接口,使得PHP可以和其他應用進行互動資料。
1)PHP執行的兩個階段,開始和結束
開始階段:
a. 子產品初始化階段(MINIT), 在整個SAPI生命周期内(例如Apache啟動以後的整個生命周期内或者指令行程式整個執行過程中), 該過程隻進行一次
b. 子產品激活階段(RINIT),該過程發生在請求階段, 例如通過url請求某個頁面,則在每次請求之前都會進行子產品激活(RINIT請求開始)
結束階段:
a. 停用子產品(RSHUTDOWN,對應RINIT)
b. 在SAPI生命周期結束(Web伺服器退出或者指令行腳本執行完畢退出)時關閉子產品(MSHUTDOWN,對應MINIT)
2)單程序SAPI生命周期
CLI/CGI模式的PHP屬于單程序的SAPI模式。這類的請求在處理一次請求後就關閉。也就是隻會經過如下幾個環節:
開始 - 請求開始 - 請求關閉 - 結束。SAPI接口實作就完成了其生命周期。
a. 啟動:初始化若幹全局變量,初始化若幹常量,初始化Zend引擎和核心元件,解析php.ini,全局操作函數的初始化,初始化靜态建構的子產品和共享子產品(MINIT),禁用函數和類
b. 激活:激活Zend引擎,激活SAPI,環境初始化,子產品請求初始化
c. 運作:要解析執行的檔案,需要做詞法分析、文法分析和中間代碼生成操作,傳回此檔案的所有中間代碼
d. 關閉:關閉請求的過程是一個若幹個關閉操作的集合
e. 結束:flush,關閉Zend引擎
3)多程序SAPI生命周期
通常PHP是編譯為apache的一個子產品來處理PHP請求。
Apache一般會采用多程序模式, Apache啟動後會fork出多個子程序,每個程序的記憶體空間獨立,每個子程序都會經過開始和結束環節, 不過每個程序的開始階段隻在程序fork出來以來後進行,在整個程序的生命周期内可能會處理多個請求。
4)多線程的SAPI生命周期
多線程模式和多程序中的某個程序類似,不同的是在整個程序的生命周期内會并行的重複着 請求開始-請求關閉的環節
SAPI處于PHP整個架構較上層,而真正腳本的執行主要由Zend引擎來完成。
a. 如上例中, 傳遞給php程式需要執行的檔案, php程式完成基本的準備工作後啟動PHP及Zend引擎, 加載注冊的擴充子產品。
b. 初始化完成後讀取腳本檔案,Zend引擎對腳本檔案進行詞法分析,文法分析。
c. 編譯成opcode執行。 如果安裝了apc之類的opcode緩存, 編譯環節可能會被跳過而直接從緩存中讀取opcode執行。
很多程式設計語言都使用lex/yacc或他們的變體(flex/bison)來作為語言的詞法文法分析生成器, 比如PHP、Ruby、Python以及MySQL的SQL語言實作。
PHP是建構在Zend虛拟機(Zend VM)之上的。PHP的opcode就是Zend虛拟機中的指令。
參考資料:
本文轉自 咖啡機(K.F.J) 部落格園部落格,原文連結http://www.cnblogs.com/strick/p/5032943.html:,如需轉載請自行聯系原作者