深入理解php底層:php生命周期

1裂七、PHP的運(yùn)行模式:

PHP兩種運(yùn)行模式是WEB模式、CLI模式月幌。無(wú)論哪種模式碍讯,PHP工作原理都是一樣的,作為一種SAPI運(yùn)行扯躺。

1捉兴、當(dāng)我們?cè)诮K端敲入php這個(gè)命令的時(shí)候,它使用的是CLI录语。
它就像一個(gè)web服務(wù)器一樣來(lái)支持php完成這個(gè)請(qǐng)求倍啥,請(qǐng)求完成后再重新把控制權(quán)交給終端。

2澎埠、當(dāng)使用Apache或者別web服務(wù)器作為宿主時(shí)虽缕,當(dāng)一個(gè)請(qǐng)求到來(lái)時(shí),PHP會(huì)來(lái)支持完成這個(gè)請(qǐng)求蒲稳。一般有:

多進(jìn)程(通常編譯為apache的模塊來(lái)處理PHP請(qǐng)求)
多線程模式

2氮趋、一切的開(kāi)始: SAPI接口

通常我們編寫(xiě)[php]Web程序都是通過(guò)Apache或者Nginx這類(lèi)Web服務(wù)器來(lái)測(cè)試腳本. 或者在命令行下通過(guò)php程序來(lái)執(zhí)行PHP腳本. 執(zhí)行完成腳本后,服務(wù)器應(yīng)答江耀,瀏覽器顯示應(yīng)答信息,或者在命令結(jié)束后在標(biāo)準(zhǔn)輸出顯示內(nèi)容. 我們很少關(guān)心PHP解釋器在哪里. 雖然通過(guò)Web服務(wù)器和命令行程序執(zhí)行腳本看起來(lái)很不一樣. 實(shí)際上她們的工作是一樣的. 命令行程序和Web程序類(lèi)似, 命令行參數(shù)傳遞給要執(zhí)行的腳本,相當(dāng)于通過(guò)url 請(qǐng)求一個(gè)PHP頁(yè)面. 腳本戳里完成后返回響應(yīng)結(jié)果,只不過(guò)命令行響應(yīng)的結(jié)果是顯示在終端上. 腳本執(zhí)行的開(kāi)始都是通過(guò)SAPI接口進(jìn)行的.

1)剩胁、啟動(dòng)apache:當(dāng)給定的SAPI啟動(dòng)時(shí),例如在對(duì)/usr/local/apache/bin/apachectl start的響應(yīng)中祥国,PHP由初始化其內(nèi)核子系統(tǒng)開(kāi)始昵观。在接近啟動(dòng)例程的末尾,它加載每個(gè)擴(kuò)展的代碼并調(diào)用其模塊初始化例程(MINIT)舌稀。這使得每個(gè)擴(kuò)展可以初始化內(nèi)部變量啊犬、分配資源、注冊(cè)資源處理器壁查,以及向ZEND注冊(cè)自己的函數(shù)觉至,以便于腳本調(diào)用這其中的函數(shù)時(shí)候ZEND知道執(zhí)行哪些代碼。

2)睡腿、請(qǐng)求處理初始化:接下來(lái)康谆,PHP等待SAPI層請(qǐng)求要處理的頁(yè)面。對(duì)于CGI或CLI等SAPI嫉到,這將立刻發(fā)生且只發(fā)生一次沃暗。對(duì)于Apache、IIS或其他成熟的web服務(wù)器SAPI何恶,每次遠(yuǎn)程用戶(hù)請(qǐng)求頁(yè)面時(shí)都將發(fā)生孽锥,因此重復(fù)很多次,也可能并發(fā)。不管請(qǐng)求如何產(chǎn)生惜辑,PHP開(kāi)始于要求ZE建立腳本的運(yùn)行環(huán)境唬涧,然后調(diào)用每個(gè)擴(kuò)展的請(qǐng)求初始化 (RINIT)函數(shù)。RINIT使得擴(kuò)展有機(jī)會(huì)設(shè)定特定的環(huán)境變量盛撑,根據(jù)請(qǐng)求分配資源碎节,或者執(zhí)行其他任務(wù),如審核抵卫。 session擴(kuò)展中有個(gè)RINIT作用的典型示例狮荔,如果啟用了session.auto_start選項(xiàng),RINIT將自動(dòng)觸發(fā)用戶(hù)空間的session_start()函數(shù)以及預(yù)組裝$_SESSION變量介粘。

3)殖氏、執(zhí)行php代碼: 一旦請(qǐng)求被初始化了,ZE開(kāi)始接管控制權(quán)姻采,將PHP腳本翻譯成符號(hào)雅采,最終形成操作碼并逐步運(yùn)行之。如任一操作碼需要調(diào)用擴(kuò)展的函數(shù)慨亲,ZE將會(huì)把參數(shù)綁定到該函數(shù)婚瓜,并且臨時(shí)交出控制權(quán)直到函數(shù)運(yùn)行結(jié)束。

4)刑棵、腳本結(jié)束:腳本運(yùn)行結(jié)束后巴刻,PHP調(diào)用每個(gè)擴(kuò)展的請(qǐng)求關(guān)閉(RSHUTDOWN)函數(shù)以執(zhí)行最后的清理工作(如將session變量存入磁盤(pán))。接下來(lái)铐望,ZE執(zhí)行清理過(guò)程(垃圾收集)-有效地對(duì)之前的請(qǐng)求期間用到的每個(gè)變量執(zhí)行unset()冈涧。

5)茂附、sapi關(guān)閉:一旦完成正蛙,PHP繼續(xù)等待SAPI的其他文檔請(qǐng)求或者是關(guān)閉信號(hào)。對(duì)于CGI和CLI等SAPI营曼,沒(méi)有“下一個(gè)請(qǐng)求”乒验,所以SAPI立刻開(kāi)始關(guān)閉。關(guān)閉期間蒂阱,PHP再次遍歷每個(gè)擴(kuò)展锻全,調(diào)用其模塊關(guān)閉(MSHUTDOWN)函數(shù),并最終關(guān)閉自己的內(nèi)核子系統(tǒng)录煤。

簡(jiǎn)要的過(guò)程如下:
1. PHP是隨著Apache的啟動(dòng)而運(yùn)行的鳄厌;
2. PHP通過(guò)mod_php5.so模塊和Apache相連(具體說(shuō)來(lái)是SAPI,即服務(wù)器應(yīng)用程序編程接口)妈踊;
3. PHP總共有三個(gè)模塊:內(nèi)核了嚎、Zend引擎、以及擴(kuò)展層;
4. PHP內(nèi)核用來(lái)處理請(qǐng)求歪泳、文件流萝勤、錯(cuò)誤處理等相關(guān)操作;
5. Zend引擎(ZE)用以將源文件轉(zhuǎn)換成機(jī)器語(yǔ)言呐伞,然后在虛擬機(jī)上運(yùn)行它敌卓;
6. 擴(kuò)展層是一組函數(shù)、類(lèi)庫(kù)和流伶氢,PHP使用它們來(lái)執(zhí)行一些特定的操作趟径。比如,我們需要mysql擴(kuò)展來(lái)連接MySQL數(shù)據(jù)庫(kù)鞍历;
7. 當(dāng)ZE執(zhí)行程序時(shí)可能會(huì)需要連接若干擴(kuò)展舵抹,這時(shí)ZE將控制權(quán)交給擴(kuò)展,等處理完特定任務(wù)后再返還劣砍;
8. 最后惧蛹,ZE將程序運(yùn)行結(jié)果返回給PHP內(nèi)核,它再將結(jié)果傳送給SAPI層刑枝,最終輸出到瀏覽器上香嗓。

3、PHP的開(kāi)始和結(jié)束階段

開(kāi)始階段有兩個(gè)過(guò)程:

第一個(gè)過(guò)程:apache啟動(dòng)的過(guò)程装畅,即在任何請(qǐng)求到達(dá)之前就發(fā)生靠娱。是在整個(gè)SAPI生命周期內(nèi)(例如Apache啟動(dòng)以后的整個(gè)生命周期內(nèi)或者命令行程序整個(gè)執(zhí)行過(guò)程中)的開(kāi)始階段(MINIT),該階段只進(jìn)行一次.。啟動(dòng)Apache后掠兄,PHP解釋程序也隨之啟動(dòng)像云; PHP調(diào)用各個(gè)擴(kuò)展(模塊)的MINIT方法,從而使這些擴(kuò)展切換到可用狀態(tài)蚂夕⊙肝埽看看php.ini文件里打開(kāi)了哪些擴(kuò)展吧; MINIT的意思是“模塊初始化”婿牍。各個(gè)模塊都定義了一組函數(shù)侈贷、類(lèi)庫(kù)等用以處理其他請(qǐng)求。 模塊在這個(gè)階段可以進(jìn)行一些初始化工作,例如注冊(cè)常量, 定義模塊使用的類(lèi)等等.典型的的模塊回調(diào)函數(shù)MINIT方法如下:

PHP_MINIT_FUNCTION(myphpextension) { /* Initialize functions, classes etc */ }  
{  
    // 注冊(cè)常量或者類(lèi)等初始化操作   
    return SUCCESS;   
}  

第二個(gè)過(guò)程發(fā)生在請(qǐng)求階段,當(dāng)一個(gè)頁(yè)面請(qǐng)求發(fā)生時(shí).則在每次請(qǐng)求之前都會(huì)進(jìn)行初始化過(guò)程(RINIT請(qǐng)求開(kāi)始).

請(qǐng)求到達(dá)之后等脂,SAPI層將控制權(quán)交給PHP層俏蛮,PHP初始化本次請(qǐng)求執(zhí)行腳本所需的環(huán)境變量,例如創(chuàng)建一個(gè)執(zhí)行環(huán)境,包括保存php運(yùn)行過(guò)程中變量名稱(chēng)和變量值內(nèi)容的符號(hào)表. 以及當(dāng)前所有的函數(shù)以及類(lèi)等信息的符號(hào)表.例如是Session模塊的RINIT,如果在php.ini中啟用了Session 模塊上遥,那在調(diào)用該模塊的RINIT時(shí)就會(huì)初始化$_SESSION變量搏屑,并將相關(guān)內(nèi)容讀入; 然后PHP會(huì)調(diào)用所有模塊RINIT函數(shù),即“請(qǐng)求初始化”粉楚。 在這個(gè)階段各個(gè)模塊也可以執(zhí)行一些相關(guān)的操作, 模塊的RINIT函數(shù)和MINIT函數(shù)類(lèi)似 辣恋,RINIT方法可以看作是一個(gè)準(zhǔn)備過(guò)程,在程序執(zhí)行之間就會(huì)自動(dòng)啟動(dòng)。

PHP_RINIT_FUNCTION(myphpextension)  
{  
    // 例如記錄請(qǐng)求開(kāi)始時(shí)間   
    // 隨后在請(qǐng)求結(jié)束的時(shí)候記錄結(jié)束時(shí)間.這樣我們就能夠記錄下處理請(qǐng)求所花費(fèi)的時(shí)間了  
    return SUCCESS;   
}  

結(jié)束階段分為兩個(gè)環(huán)節(jié):

請(qǐng)求處理完后就進(jìn)入了結(jié)束階段, 一般腳本執(zhí)行到末尾或者通過(guò)調(diào)用exit()或者die()函數(shù),PHP都將進(jìn)入結(jié)束階段. 和開(kāi)始階段對(duì)應(yīng),結(jié)束階段也分為兩個(gè)環(huán)節(jié),一個(gè)在請(qǐng)求結(jié)束后(RSHUWDOWN),一個(gè)在SAPI生命周期結(jié)束時(shí)(MSHUTDOWN).

第一個(gè)環(huán)節(jié):請(qǐng)求處理完后結(jié)束階段:請(qǐng)求處理完后就進(jìn)入了結(jié)束階段抑党,PHP就會(huì)啟動(dòng)清理程序包警。它會(huì)按順序調(diào)用各個(gè)模塊的RSHUTDOWN方法。 RSHUTDOWN用以清除程序運(yùn)行時(shí)產(chǎn)生的符號(hào)表底靠,也就是對(duì)每個(gè)變量調(diào)用unset函數(shù)害晦。典型的RSHUTDOWN方法如下:

PHP_RSHUTDOWN_FUNCTION(myphpextension)  
{  
    // 例如記錄請(qǐng)求結(jié)束時(shí)間, 并把相應(yīng)的信息寫(xiě)入到日至文件中.  
    return SUCCESS;   
}  

第二個(gè)環(huán)節(jié):最后,所有的請(qǐng)求都已處理完畢暑中,SAPI也準(zhǔn)備關(guān)閉了壹瘟, PHP調(diào)用每個(gè)擴(kuò)展的MSHUTDOWN方法,這是各個(gè)模塊最后一次釋放內(nèi)存的機(jī)會(huì)鳄逾。(這個(gè)是對(duì)于CGI和CLI等SAPI稻轨,沒(méi)有“下一個(gè)請(qǐng)求”,所以SAPI立刻開(kāi)始關(guān)閉雕凹。)

典型的RSHUTDOWN方法如下:

PHP_MSHUTDOWN_FUNCTION(extension_name) {   
    /* Free handlers and persistent memory etc */   
    return SUCCESS;   
}  

這樣殴俱,整個(gè)PHP生命周期就結(jié)束了。要注意的是枚抵,只有在服務(wù)器沒(méi)有請(qǐng)求的情況下才會(huì)執(zhí)行“啟動(dòng)第一步”和“關(guān)閉第二步”线欲。

SAPI運(yùn)行PHP都經(jīng)過(guò)下面幾個(gè)階段:
1、模塊初始化階段(Module init):
即調(diào)用每個(gè)拓展源碼中的的PHP_MINIT_FUNCTION中的方法初始化模塊,進(jìn)行一些模塊所需變量的申請(qǐng),內(nèi)存分配等汽摹。
2李丰、請(qǐng)求初始化階段(Request init):
即接受到客戶(hù)端的請(qǐng)求后調(diào)用每個(gè)拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP腳本的執(zhí)行環(huán)境。
3逼泣、執(zhí)行PHP腳本
4趴泌、請(qǐng)求結(jié)束(Request Shutdown) :
這時(shí)候調(diào)用每個(gè)拓展的PHP_RSHUTDOWN_FUNCTION方法清理請(qǐng)求現(xiàn)場(chǎng),并且ZE開(kāi)始回收變量和內(nèi)存。
5拉庶、關(guān)閉模塊(Module shutdown) :
Web服務(wù)器退出或者命令行腳本執(zhí)行完畢退出會(huì)調(diào)用拓展源碼中的PHP_MSHUTDOWN_FUNCTION 方法

4嗜憔、單進(jìn)程SAPI生命周期

CLI/CGI模式的PHP屬于單進(jìn)程的SAPI模式。這類(lèi)的請(qǐng)求在處理一次請(qǐng)求后就關(guān)閉砍的。也就是只會(huì)經(jīng)過(guò)如下幾個(gè)環(huán)節(jié): 開(kāi)始 - 請(qǐng)求開(kāi)始 - 請(qǐng)求關(guān)閉 - 結(jié)束 SAPI接口實(shí)現(xiàn)就完成了其生命周期痹筛。如圖所示:

image.png

5莺治、多進(jìn)程SAPI生命周期

通常PHP是編譯為apache的一個(gè)模塊來(lái)處理PHP請(qǐng)求廓鞠。Apache一般會(huì)采用多進(jìn)程模式, Apache啟動(dòng)后會(huì)fork出多個(gè)子進(jìn)程谣旁,每個(gè)進(jìn)程的內(nèi)存空間獨(dú)立床佳,每個(gè)子進(jìn)程都會(huì)經(jīng)過(guò)開(kāi)始和結(jié)束環(huán)節(jié), 不過(guò)每個(gè)進(jìn)程的開(kāi)始階段只在進(jìn)程fork出來(lái)以后進(jìn)行榄审,在整個(gè)進(jìn)程的生命周期內(nèi)可能會(huì)處理多個(gè)請(qǐng)求砌们。 只有在Apache關(guān)閉或者進(jìn)程被結(jié)束之后才會(huì)進(jìn)行關(guān)閉階段,在這兩個(gè)階段之間會(huì)隨著每個(gè)請(qǐng)求重復(fù)請(qǐng)求開(kāi)始-請(qǐng)求關(guān)閉的環(huán)節(jié)。

如圖所示:


image.png

6浪感、多線程的SAPI生命周期

多線程模式和多進(jìn)程中的某個(gè)進(jìn)程類(lèi)似昔头,不同的是在整個(gè)進(jìn)程的生命周期內(nèi)會(huì)并行的重復(fù)著 請(qǐng)求開(kāi)始-請(qǐng)求關(guān)閉的環(huán)節(jié).

在這種模式下,只有一個(gè)服務(wù)器進(jìn)程在運(yùn)行著影兽,但會(huì)同時(shí)運(yùn)行很多線程揭斧,這樣可以減少一些資源開(kāi)銷(xiāo),向Module init和Module shutdown就只需要運(yùn)行一遍就行了峻堰,一些全局變量也只需要初始化一次讹开,因?yàn)榫€程獨(dú)具的特質(zhì),使得各個(gè)請(qǐng)求之間方便的共享一些數(shù)據(jù)成為可能捐名。

多線程工作方式如下圖:


image.png

7旦万、Apache一般使用多進(jìn)程模式prefork

在linux下使用#http –l 命令可以查看當(dāng)前使用的工作模式。也可以使用#apachectl -l命令镶蹋。
看到的prefork.c成艘,說(shuō)明使用的prefork工作模式。

prefork 進(jìn)程池模型贺归,用在 UNIX 和類(lèi)似的系統(tǒng)上比較多狰腌,主要是由于寫(xiě)起來(lái)方便,也容易移植牧氮,還不容易出問(wèn)題琼腔。要知道,如果采用線程模型的話踱葛,用戶(hù)線程丹莲、內(nèi)核線程和混合型線程有不同的特性,移植起來(lái)就麻煩尸诽。prefork 模型甥材,即預(yù)先 fork() 出來(lái)一些子進(jìn)程緩沖一下,用一個(gè)鎖來(lái)控制同步性含,連接到來(lái)了就放行一個(gè)子進(jìn)程洲赵,讓它去處理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末商蕴,一起剝皮案震驚了整個(gè)濱河市叠萍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绪商,老刑警劉巖苛谷,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異格郁,居然都是意外死亡腹殿,警方通過(guò)查閱死者的電腦和手機(jī)独悴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锣尉,“玉大人刻炒,你說(shuō)我怎么就攤上這事∽圆祝” “怎么了落蝙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)暂幼。 經(jīng)常有香客問(wèn)我筏勒,道長(zhǎng),這世上最難降的妖魔是什么旺嬉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任管行,我火速辦了婚禮,結(jié)果婚禮上邪媳,老公的妹妹穿的比我還像新娘捐顷。我一直安慰自己,他們只是感情好雨效,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布迅涮。 她就那樣靜靜地躺著,像睡著了一般徽龟。 火紅的嫁衣襯著肌膚如雪叮姑。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天据悔,我揣著相機(jī)與錄音传透,去河邊找鬼。 笑死极颓,一個(gè)胖子當(dāng)著我的面吹牛朱盐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菠隆,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兵琳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了骇径?” 一聲冷哼從身側(cè)響起躯肌,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎既峡,沒(méi)想到半個(gè)月后羡榴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體碧查,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡运敢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年校仑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片传惠。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡迄沫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卦方,到底是詐尸還是另有隱情羊瘩,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布盼砍,位于F島的核電站尘吗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏浇坐。R本人自食惡果不足惜睬捶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望近刘。 院中可真熱鬧擒贸,春花似錦、人聲如沸觉渴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)案淋。三九已至座韵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間踢京,已是汗流浹背回右。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漱挚,地道東北人翔烁。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像旨涝,于是被迫代替她去往敵國(guó)和親蹬屹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容