深入理解php底層 php生命週期
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
1、PHP的執行模式:
PHP兩種執行模式是WEB模式、CLI模式。無論哪種模式,PHP工作原理都是一樣的,作為一種SAPI執行。
1、當我們在終端敲入
它就像一個web伺服器一樣來支援php完成這個請求,請求完成後再重新把控制權交給終端。
2、當使用Apache或者別web伺服器作為宿主時,當一個請求到來時,PHP會來支援完成這個請求。一般有:
多程序(通常編譯為apache的模組來處理PHP請求)
多執行緒模式
2、一切的開始: SAPI介面
通常我們編寫php Web程式都是通過Apache或者Nginx這類Web伺服器來測試指令碼. 或者在命令列下通過php程式來執行PHP指令碼. 執行完成指令碼後,伺服器應答,瀏覽器顯示應答資訊,或者在命令結束後在標準輸出顯示內容. 我們很少關心PHP直譯器在哪裡. 雖然通過Web伺服器和命令列程式執行指令碼看起來很不一樣. 實際上她們的工作是一樣的. 命令列程式和Web程式類似, 命令列引數傳遞給要執行的指令碼,相當於通過url 請求一個PHP頁面. 指令碼戳裡完成後返回響應結果,只不過命令列響應的結果是顯示在終端上. 指令碼執行的開始都是通過SAPI介面進行的.
1)、啟動apache:當給定的SAPI啟動時,例如在對/usr/local/apache/bin/apachectl start的響應中,PHP由初始化其核心子系統開始。在接近啟動例程的末尾,它載入每個擴充套件的程式碼並呼叫其模組初始化例程(MINIT)。這使得每個擴充套件可以初始化內部變數、分配資源、註冊資源處理器,以及向ZE註冊自己的函式,以便於指令碼呼叫這其中的函式時候ZE知道執行哪些程式碼。
2)、請求處理初始化:接下來,PHP等待SAPI層請求要處理的頁面。對於CGI或CLI等SAPI,這將立刻發生且只發生一次。對於Apache、IIS或其他成熟的web
3)、執行php程式碼: 一旦請求被初始化了,ZE開始接管控制權,將PHP指令碼翻譯成符號,最終形成操作碼並逐步執行之。如任一操作碼需要呼叫擴充套件的函式,ZE將會把引數繫結到該函式,並且臨時交出控制權直到函式執行結束。
4)、指令碼結束:指令碼執行結束後,PHP呼叫每個擴充套件的請求關閉(RSHUTDOWN)函式以執行最後的清理工作(如將session變數存入磁碟)。接下來,ZE執行清理過程(垃圾收集)-有效地對之前的請求期間用到的每個變數執行unset()。
5)、sapi關閉:一旦完成,PHP繼續等待SAPI的其他文件請求或者是關閉訊號。對於CGI和CLI等SAPI,沒有“下一個請求”,所以SAPI立刻開始關閉。關閉期間,PHP再次遍歷每個擴充套件,呼叫其模組關閉(MSHUTDOWN)函式,並最終關閉自己的核心子系統。
簡要的過程如下:
1. PHP是隨著Apache的啟動而執行的;
2. PHP通過mod_php5.so模組和Apache相連(具體說來是SAPI,即伺服器應用程式程式設計介面);
3. PHP總共有三個模組:核心、Zend引擎、以及擴充套件層;
4. PHP核心用來處理請求、檔案流、錯誤處理等相關操作;
5. Zend引擎(ZE)用以將原始檔轉換成機器語言,然後在虛擬機器上執行它;
6. 擴充套件層是一組函式、類庫和流,PHP使用它們來執行一些特定的操作。比如,我們需要mysql擴充套件來連線MySQL資料庫;
7. 當ZE執行程式時可能會需要連線若干擴充套件,這時ZE將控制權交給擴充套件,等處理完特定任務後再返還;
8. 最後,ZE將程式執行結果返回給PHP核心,它再將結果傳送給SAPI層,最終輸出到瀏覽器上。
3、PHP的開始和結束階段
開始階段有兩個過程:
第一個過程:apache啟動的過程,即在任何請求到達之前就發生。是在整個SAPI生命週期內(例如Apache啟動以後的整個生命週期內或者命令列程式整個執行過程中)的開始階段(MINIT),該階段只進行一次.。啟動Apache後,PHP解釋程式也隨之啟動; PHP呼叫各個擴充套件(模組)的MINIT方法,從而使這些擴充套件切換到可用狀態。看看php.ini檔案裡打開了哪些擴充套件吧; MINIT的意思是“模組初始化”。各個模組都定義了一組函式、類庫等用以處理其他請求。 模組在這個階段可以進行一些初始化工作,例如註冊常量, 定義模組使用的類等等.典型的的模組回撥函式MINIT方法如下:
PHP_MINIT_FUNCTION(myphpextension) { /* Initialize functions, classes etc */ }{ // 註冊常量或者類等初始化操作 return SUCCESS; }
第二個過程發生在請求階段,當一個頁面請求發生時.則在每次請求之前都會進行初始化過程(RINIT請求開始).
請求到達之後,SAPI層將控制權交給PHP層,PHP初始化本次請求執行指令碼所需的環境變數,例如建立一個執行環境,包括儲存php執行過程中變數名稱和變數值內容的符號表. 以及當前所有的函式以及類等資訊的符號表.例如是Session模組的RINIT,如果在php.ini中啟用了Session 模組,那在呼叫該模組的RINIT時就會初始化$_SESSION變數,並將相關內容讀入; 然後PHP會呼叫所有模組RINIT函式,即“請求初始化”。 在這個階段各個模組也可以執行一些相關的操作, 模組的RINIT函式和MINIT函式類似 ,RINIT方法可以看作是一個準備過程,在程式執行之間就會自動啟動。
PHP_RINIT_FUNCTION(myphpextension){ // 例如記錄請求開始時間 // 隨後在請求結束的時候記錄結束時間.這樣我們就能夠記錄下處理請求所花費的時間了 return SUCCESS; }
結束階段分為兩個環節:
請求處理完後就進入了結束階段, 一般指令碼執行到末尾或者通過呼叫exit()或者die()函式,PHP都將進入結束階段. 和開始階段對應,結束階段也分為兩個環節,一個在請求結束後(RSHUWDOWN),一個在SAPI生命週期結束時(MSHUTDOWN).第一個環節:請求處理完後結束階段:請求處理完後就進入了結束階段,PHP就會啟動清理程式。它會按順序呼叫各個模組的RSHUTDOWN方法。 RSHUTDOWN用以清除程式執行時產生的符號表,也就是對每個變數呼叫unset函式。典型的RSHUTDOWN方法如下:
PHP_RSHUTDOWN_FUNCTION(myphpextension){ // 例如記錄請求結束時間, 並把相應的資訊寫入到日至檔案中. return SUCCESS; }
第二個環節:最後,所有的請求都已處理完畢,SAPI也準備關閉了, PHP呼叫每個擴充套件的MSHUTDOWN方法,這是各個模組最後一次釋放記憶體的機會。(這個是對於CGI和CLI等SAPI,沒有“下一個請求”,所以SAPI立刻開始關閉。)
典型的RSHUTDOWN方法如下:
PHP_MSHUTDOWN_FUNCTION(extension_name) { /* Free handlers and persistent memory etc */ return SUCCESS; }
這樣,整個PHP生命週期就結束了。要注意的是,只有在伺服器沒有請求的情況下才會執行“啟動第一步”和“關閉第二步”。
SAPI執行PHP都經過下面幾個階段:
1、模組初始化階段(Module init) :
即呼叫每個拓展原始碼中的的PHP_MINIT_FUNCTION中的方法初始化模組,進行一些模組所需變數的申請,記憶體分配等。
2、請求初始化階段(Request init) :
即接受到客戶端的請求後呼叫每個拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP指令碼的執行環境。
3、執行PHP指令碼
4、請求結束(Request Shutdown) :
這時候呼叫每個拓展的PHP_RSHUTDOWN_FUNCTION方法清理請求現場,並且ZE開始回收變數和記憶體。
5、關閉模組(Module shutdown) :
Web伺服器退出或者命令列指令碼執行完畢退出會呼叫拓展原始碼中的PHP_MSHUTDOWN_FUNCTION 方法
4、單程序SAPI生命週期
CLI/CGI模式的PHP屬於單程序的SAPI模式。這類的請求在處理一次請求後就關閉。也就是隻會經過如下幾個環節: 開始 - 請求開始 - 請求關閉 - 結束 SAPI介面實現就完成了其生命週期。如圖所示:
5、多程序SAPI生命週期
通常PHP是編譯為apache的一個模組來處理PHP請求。Apache一般會採用多程序模式, Apache啟動後會
fork出多個子程序,每個程序的記憶體空間獨立,每個子程序都會經過開始和結束環節, 不過每個程序的開始階
段只在程序fork出來以來後進行,在整個程序的生命週期內可能會處理多個請求。 只有在Apache關閉或者程序
被結束之後才會進行關閉階段,在這兩個階段之間會隨著每個請求重複請求開始-請求關閉的環節。
如圖所示:
6、多執行緒的SAPI生命週期
多執行緒模式和多程序中的某個程序類似,不同的是在整個程序的生命週期內會並行的重複著 請求開始-請求關 閉的環節.
在這種模式下,只有一個伺服器程序在執行著,但會同時執行很多執行緒,這樣可以減少一些資源開銷,向Module init和Module shutdown就只需要執行一遍就行了,一些全域性變數也只需要初始化一次,因為執行緒獨具的特質,使得各個請求之間方便的共享一些資料成為可能。
多執行緒工作方式如下圖
7、Apache一般使用多程序模式prefork
在linux下使用#http –l 命令可以檢視當前使用的工作模式。也可以使用#apachectl -l命令。
看到的prefork.c,說明使用的prefork工作模式。
prefork 程序池模型,用在 UNIX 和類似的系統上比較多,主要是由於寫起來方便,也容易移植,還不容易出問題。要知道,如果採用執行緒模型的話,使用者執行緒、核心執行緒和混合型執行緒有不同的特性,移植起來就麻煩。prefork 模型,即預先 fork() 出來一些子程序緩衝一下,用一個鎖來控制同步,連線到來了就放行一個子程序,讓它去處理。
prefork MPM 使用多個子程序,每個子程序只有一個執行緒。每個程序在某個確定的時間只能維持一個連線。在大多數平臺上,Prefork MPM在效率上要比Worker MPM要高,但是記憶體使用大得多。prefork的無執行緒設計在某些情況下將比worker更有優勢:他能夠使用那些沒有處理好執行緒安全的第三方模組,並 且對於那些執行緒除錯困難的平臺而言,他也更容易除錯一些。