探究php底层运行机制
本文轉載自:http://www.myext.cn/Article/921.html
概要
簡介
先看看下面這個過程:
我們從未手動開啟過PHP的相關進程,它是隨著Apache的啟動而運行的;?
php通過mod_php5.so模塊和Apache相連(具體說來是SAPI,即服務器應用程序編程接口);?
PHP總共有三個模塊:內核、Zend引擎、以及擴展層;?
PHP內核用來處理請求、文件流、錯誤處理等相關操作;?
Zend引擎(ZE)用以將源文件轉換成機器語言,然后在虛擬機上運行它;?
擴展層是一組函數、類庫和流,PHP使用它們來執行一些特定的操作。比如,我們需要MySQL擴展來連接mysql數據庫;?
當ZE執行程序時可能會需要連接若干擴展,這時ZE將控制權交給擴展,等處理完特定任務后再返還;
最后,ZE將程序運行結果返回給PHP內核,它再將結果傳送給SAPI層,最終輸出到瀏覽器上。?
深入探討
等等,沒有這么簡單。以上過程只是個簡略版,讓我們再深入挖掘一下,看看幕后還發生了些什么。
Apache啟動后,PHP解釋程序也隨之啟動;?
PHP的啟動過程有兩步;?
第一步是初始化一些環境變量,這將在整個SAPI生命周期中發生作用;?
第二步是生成只針對當前請求的一些變量設置。?
PHP啟動第一步
不清楚什么第一第二步是什么?別擔心,我們接下來詳細討論一下。讓我們先看看第一步,也是最主要的一步。要記住的是,第一步的操作在任何請求到達之前就發生了。
啟動Apache后,PHP解釋程序也隨之啟動;?
PHP調用各個擴展的MINIT方法,從而使這些擴展切換到可用狀態??纯磒hp.ini文件里打開了哪些擴展吧;?
MINIT的意思是“模塊初始化”。各個模塊都定義了一組函數、類庫等用以處理其他請求。?
一個典型的MINIT方法如下:
PHP_MINIT_FUNCTION(extension_name){
/* Initialize functions, classes etc */
}
PHP啟動第二步
當一個頁面請求發生時,SAPI層將控制權交給PHP層。于是PHP設置了用于回復本次請求所需的環境變量。同時,它還建立一個變量表,用來存放執行過程中產生的變量名和值。?
PHP調用各個模塊的RINIT方法,即“請求初始化”。一個經典的例子是Session模塊的RINIT,如果在php.ini中啟用了Session模塊,那在調用該模塊的RINIT時就會初始化$_SESSION變量,并將相關內容讀入;?
RINIT方法可以看作是一個準備過程,在程序執行之間就會自動啟動。?
一個典型的RINIT方法如下:
PHP_RINIT_FUNCTION(extension_name) {
/* Initialize session variables, pre-populate variables, redefine global variables etc */
}
PHP關閉第一步
如同PHP啟動一樣,PHP的關閉也分兩步:
一旦頁面執行完畢(無論是執行到了文件末尾還是用exit或die函數中止),PHP就會啟動清理程序。它會按順序調用各個模塊的RSHUTDOWN方法。?
RSHUTDOWN用以清除程序運行時產生的符號表,也就是對每個變量調用unset函數。?
一個典型的RSHUTDOWN方法如下:
PHP_RSHUTDOWN_FUNCTION(extension_name) {
/* Do memory management, unset all variables used in the last PHP call etc */
}
PHP關閉第二步
最后,所有的請求都已處理完畢,SAPI也準備關閉了,PHP開始執行第二步:
PHP調用每個擴展的MSHUTDOWN方法,這是各個模塊最后一次釋放內存的機會。?
一個典型的RSHUTDOWN方法如下:
PHP_MSHUTDOWN_FUNCTION(extension_name) {
/* Free handlers and persistent memory etc */
}
這樣,整個PHP生命周期就結束了。要注意的是,只有在服務器沒有請求的情況下才會執行“啟動第一步”和“關閉第二步”。
一、開篇
在開始這個專題之前,先說一點題外話。大多數人學習編程語言的時候,首先關注的是這種語言的語法及其常用函數。我學習C,Java,Php等語言就是按照這樣的方式開始的。一般情況下,這個階段需要一個月左右的時間就會完全掌握,并能基本熟練地使用。對于已有經驗的同學,可能時間更短。其實各種語言的語法和常用函數都差別不大,有很多相通的地方。如果您在學習一種編程語言的時候,拿一些真正的項目任務作為實踐,效果更佳,實踐遠勝于理論。
我們在掌握了一門編程語言之后,又會向兩個方向發展:一個方向是向上延伸,從事系統框架結構的探索;另一方向是向下延伸,從事系統底層方面的研究,我大體畫了一下這個學習演變過程的示意圖。
?
?
?
注:雖然我的形象一直用著“高高手”,但我只是個菜鳥,如有雷同,純屬巧合,歡迎善意拍磚。
?
php的語法非常簡單,正是它的簡單性,使它成為了當前互聯網第一編程語言。你不需要具備很多的知識就能上手,比如:你學習C語言,就必須非常了解各個變量如何定義,指針如何操作,內存如何創建銷毀等等。再比如:你學習Java語言,就必須具有面向對象(OO)的基礎,就必須清楚是什么時候需要封裝,什么時候需要繼承,什么時候需要多態,要做項目,怎么還得懂點SSH。Php的大部分使用者可能根本就沒這么多講究,有的人喜歡面向過程,那你就用面向過程的方式來寫代碼;有的人喜歡面向對象,那你就用面向對象的方式寫代碼。Php的產生緣于互聯網,目前也是互聯網Web2.0第一編程語言。滿足用戶需求永遠是第一位的,可維護性暫且可以放在第二位。我們通常說Web應用永遠是β版的,計劃遠沒有變化快。
我們公司里有很多phper,我曾經問過他們:“php程序到底是如何被執行的?”,多數人似乎很難說得清楚。這種情況,其實并不奇怪,我曾經拿類似的問題問過Javaer,Javaer的回答也是如此。有的同學會問:“研究這樣的問題有沒有實際意義呢?”我說:“有!”。理解系統的底層,有助于你寫出高效健壯的代碼,你會更清楚程序的代碼到底該怎么去寫。另外,如果你有志去做php擴展,那就更不必說,責無旁貸。
要回答以上問題,我覺得最好的辦法是閱讀一下php的源碼,從“根”上解決。近來我找了點時間,粗讀了一遍,愿意與各位共享。
關于php的底層工作原理,一定繞不開webserver,象apache,lighttpd,nginx,iis等。我這里就選擇apache為例吧。以下內容將結合apache的源碼、工作原理和擴展來逐步切入php的解析過程。
?
?
二、Apache運行機制剖析
l???????? B/S交互過程
瀏覽器(Browser)和服務器(Web Server)的交互過程:
?
?
?
1、? 瀏覽器向服務器發出HTTP請求(Request)。
2、? 服務器收到瀏覽器的請求數據,經過分析處理,向瀏覽器輸出響應數據(Response)。
3、? 瀏覽器收到服務器的響應數據,經過分析處理,將最終結果顯示在瀏覽器中。
?下圖是一份瀏覽器請求數據和服務器響應數據的快照:
?
?
關于瀏覽器和服務器數據交互過程非常簡單,很容易理解。我想從事Web開發的人員都很清楚,在此不再贅述,僅供參考。
?
l???????? Apache概述
Apache是目前世界上使用最為廣泛的一種Web Server,它以跨平臺、高效和穩定而聞名。按照去年官方統計的數據,Apache服務器的裝機量占該市場60%以上的份額。尤其是在X(Unix/Linux)平臺上,Apache是最常見的選擇。其它的Web Server產品,比如IIS,只能運行在Windows平臺上,是基于微軟.Net架構技術的不二選擇。
Apache并不是沒有缺點,它最為詬病的一點就是變得越來越重,被普遍認為是重量級的WebServer。所以,近年來又涌現出了很多輕量級的替代產品,比如lighttpd,nginx等等,這些WebServer的優點是運行效率很高,但缺點也很明顯,成熟度往往要低于Apache,通常只能用于某些特定場合,。
?
l???????? Apache組件邏輯圖
Apache是基于模塊化設計的,總體上看起來代碼的可讀性高于php的代碼,它的核心代碼并不多,大多數的功能都被分散到各個模塊中,各個模塊在系統啟動的時候按需載入。你如果想要閱讀Apache的源代碼,建議你直接從main.c文件讀起,系統最主要的處理邏輯都包含在里面。MPM(Multi?-Processing?Modules,多重處理模塊)是Apache的核心組件之一,Apache通過MPM來使用操作系統的資源,對進程和線程池進行管理。Apache為了能夠獲得最好的運行性能,針對不同的平臺(Unix/linux、Window)做了優化,為不同的平臺提供了不同的MPM,用戶可以根據實際情況進行選擇,其中最常使用的MPM有prefork和worker兩種。至于您的服務器正以哪種方式運行,取決于安裝Apache過程中指定的MPM編譯參數,在X系統上默認的編譯參數為prefork。由于大多數的Unix都不支持真正的線程,所以采用了預派生子進程(prefork)方式,象Windows或者Solaris這些支持線程的平臺,基于多進程多線程混合的worker模式是一種不錯的選擇。對此感興趣的同學可以閱讀有關資料,此處不再多講。Apache中還有一個重要的組件就是APR(Apache portable Runtime Library),即Apache可移植運行庫,它是一個對操作系統調用的抽象庫,用來實現Apache內部組件對操作系統的使用,提高系統的可移植性。Apache對于php的解析,就是通過眾多Module中的php Module來完成的。
?
?
Apache的邏輯構成以及與操作系統的關系
?
l???????? Apache的生命周期
這一節的內容會與php模塊的載入有關,您可以略微關注一下。以下是Apache的生命周期(prefork模式)示意圖。
?
?
?
?
l???????? Apache的生命周期
這一節的內容將會闡述php模塊的載入過程,請參考Apache的生命周期示意圖(prefork模式下)。
?
Apache的運行分為啟動階段和運行階段。
?
1.???????? 啟動階段
在啟動階段,Apache主要進行配置文件解析(例如http.conf以及Include指令設定的配置文件等)、模塊加載(例如mod_php.so,mod_perl.so等)和系統資源初始化(例如日志文件、共享內存段等)工作。
在這個階段,Apache為了獲得系統資源最大的使用權限,將以特權用戶root(X系統)或超級管理員administrator(Windows系統)完成啟動。
?
Apache和“php處理機”的裝配過程就是在這個階段完成的。
“php處理機”就是負責解釋和執行你的php代碼的系統模塊。這個名字是我特意創造的,目的是為了幫助你理解本節的內容,后面的章節還會給出更專業的名稱。
?
你單獨做過php的安裝配置嗎?
如果你做過類似的工作,下面的內容很容易理解;如果你沒有做過,可以嘗試安裝一下,有助于加深你的理解。不過,我的文章向來深入淺出,我會盡量把這個過程講得更淺顯一些。其實php的安裝非常簡單,如果你很感興趣的話,可以到網上隨便搜一篇安裝指南,按步驟照做就可以了。
把php最終集成到Apache系統中,還需要對Apache進行一些必要的設置。這里,我們就以php的mod_php5 SAPI運行模式為例進行講解,至于SAPI這個概念后面我們還會詳細講解。
假定我們安裝的版本是Apache2 和 Php5,那么需要編輯Apache的主配置文件http.conf,在其中加入下面的幾行內容:
Unix/Linux環境下:
LoadModule php5_module modules/mod_php5.so
AddType application/x-httpd-php .php
注:其中modules/mod_php5.so 是X系統環境下mod_php5.so文件的安裝位置。
?
Windows環境下:
LoadModule php5_module d:/php/php5apache2.dll
AddType application/x-httpd-php .php
注:其中d:/php/php5apache2.dll 是在Windows環境下php5apache2.dll文件的安裝位置。
這兩項配置就是告訴Apache Server,以后收到的Url用戶請求,凡是以php作為后綴,就需要調用php5_module模塊(mod_php5.so/ php5apache2.dll)進行處理。
?
這個過程可以參考以下的示意圖:
?
?
?
Apache啟動階段的源碼包含在server/main.c中,我整理了一下源碼中的對應關系:
?
?
?
?
不熟悉unix/linux的同學可能會問so文件(mod_php5.so)是個什么樣的文件?
unix/linux下,so后綴文件是一個DSO文件,DSO與windows系統下的dll是等價概念,都是把一堆函數封裝在一個二進制文件中。調用它們的進程把它們裝入內存后,會將其映射到自己的地址空間。
DSO全稱為Dynamic Shared Object,即動態共享對象。DLL全稱為Dynamic Link Library 即動態鏈接庫。
Apache 服務器的體系結構的最大特點,就是高度模塊化。如果你為了追求處理效率,可以把這些dso模塊在apache編譯的時候靜態鏈入,這樣會提高Apache 5%左右的處理性能。
?
2.???????? 運行階段
在運行階段,Apache主要工作是處理用戶的服務請求。
在這個階段,Apache放棄特權用戶級別,使用普通權限,這主要是基于安全性的考慮,防止由于代碼的缺陷引起的安全漏洞,象微軟的IIS就曾遭受“紅色代碼(Code Red)”和“尼姆達(Nimda)”等惡意代碼的溢出攻擊。
?
?
2、運行階段
???? 2.1 運行階段概述
??? 在運行階段,Apache主要工作是處理用戶的服務請求。
??? 在這個階段,Apache放棄特權用戶級別,使用普通權限,這主要是基于安全性的考慮,防止由于代碼的缺陷引起的安全漏洞。象微軟的IIS就曾遭受“紅色代碼(Code Red)”和“尼姆達(Nimda)”等惡意代碼的溢出攻擊。
?
??2.2 運行階段流程
??? Apache將請求處理循環分為11個階段,依次是:Post-Read-Request,URI Translation,Header Parsing,Access Control,Authentication,Authorization,MIME Type Checking,FixUp,Response,Logging,CleanUp。
?
?
???? Apache Hook機制
???? Apache的Hook機制是指:Apache 允許模塊(包括內部模塊和外部模塊,例如mod_php5.so,mod_perl.so等)將自定義的函數注入到請求處理循環中。換句話說,模塊可以在Apache的任何一個處理階段中掛接(Hook)上自己的處理函數,從而參與Apache的請求處理過程。
??? mod_php5.so/ php5apache2.dll就是將所包含的自定義函數,通過Hook機制注入到Apache中,在Apache處理流程的各個階段負責處理php請求。
??? 關于Hook機制在Windows系統開發也經常遇到,在Windows開發既有系統級的鉤子,又有應用級的鉤子。常見的翻譯軟件(例如金山詞霸等等)的屏幕取詞功能,大多數是通過安裝系統級鉤子函數完成的,將自定義函數替換gdi32.dll中的屏幕輸出的繪制函數。
?
??? Apache請求處理循環詳解
??? Apache請求處理循環的11個階段都做了哪些事情呢?
???
??? 1、Post-Read-Request階段
??? 在正常請求處理流程中,這是模塊可以插入鉤子的第一個階段。對于那些想很早進入處理請求的模塊來說,這個階段可以被利用。
??? 2、URI Translation階段
??? Apache在本階段的主要工作:將請求的URL映射到本地文件系統。模塊可以在這階段插入鉤子,執行自己的映射邏輯。mod_alias就是利用這個階段工作的。
??? 3、Header Parsing階段
??? Apache在本階段的主要工作:檢查請求的頭部。由于模塊可以在請求處理流程的任何一個點上執行檢查請求頭部的任務,因此這個鉤子很少被使用。mod_setenvif就是利用這個階段工作的。
?
??? 4、Access Control階段
??? Apache在本階段的主要工作:根據配置文件檢查是否允許訪問請求的資源。Apache的標準邏輯實現了允許和拒絕指令。mod_authz_host就是利用這個階段工作的。
??? 5、Authentication階段
???? Apache在本階段的主要工作:按照配置文件設定的策略對用戶進行認證,并設定用戶名區域。模塊可以在這階段插入鉤子,實現一個認證方法。
?
??? 6、Authorization階段
??? Apache在本階段的主要工作:根據配置文件檢查是否允許認證過的用戶執行請求的操作。模塊可以在這階段插入鉤子,實現一個用戶權限管理的方法。
??? 7、MIME Type Checking階段
??? Apache在本階段的主要工作:根據請求資源的MIME類型的相關規則,判定將要使用的內容處理函數。標準模塊mod_negotiation和mod_mime實現了這個鉤子。
??? 8、FixUp階段
??? 這是一個通用的階段,允許模塊在內容生成器之前,運行任何必要的處理流程。和Post_Read_Request類似,這是一個能夠捕獲任何信息的鉤子,也是最常使用的鉤子。
??? 9、Response階段
??? Apache在本階段的主要工作:生成返回客戶端的內容,負責給客戶端發送一個恰當的回復。這個階段是整個處理流程的核心部分。
??? 10、Logging階段
??? Apache在本階段的主要工作:在回復已經發送給客戶端之后記錄事務。模塊可能修改或者替換Apache的標準日志記錄。
?
??? 11、CleanUp階段
??? Apache在本階段的主要工作:清理本次請求事務處理完成之后遺留的環境,比如文件、目錄的處理或者Socket的關閉等等,這是Apache一次請求處理的最后一個階段。
?
??? 模塊的注入Apache的過程可以參考源碼中server/core.c文件:
?
??? mod_php5.so/ php5apache2.dll注入到Apache的函數中,最重要的就是Response階段的處理函數。
來源:http://blog.csdn.net/shupan001/article/details/7176169
總結
以上是生活随笔為你收集整理的探究php底层运行机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 贾玲个人资料(贾玲丈夫资料)
- 下一篇: php 如何快速判断一个数字属于什么范围