php程序的生命週期

一、PHP的運行模式:

    PHP兩種運行模式是WEB模式、CLI模式。不管哪一種模式,PHP工做原理都是同樣的,做爲一種SAPI運行。php

一、當咱們在終端敲入php這個命令的時候,它使用的是CLI。mysql

它就像一個web服務器同樣來支持php完成這個請求,請求完成後再從新把控制權交給終端。linux

二、當使用Apache或者別web服務器做爲宿主時,當一個請求到來時,PHP會來支持完成這個請求。通常有:web

 

    多進程(一般編譯爲apache的模塊來處理PHP請求)sql

    多線程模式數據庫

二、一切的開始: SAPI接口

    一般咱們編寫php Web程序都是經過Apache或者Nginx這類Web服務器來測試腳本. 或者在命令行下經過php程序來執行PHP腳本. 執行完成腳本後,服務器應答,瀏覽器顯示應答信息,或者在命令結束後在標準輸出顯示內容. 咱們不多關心PHP解釋器在哪裏. 雖然經過Web服務器和命令行程序執行腳本看起來很不同. 實際上她們的工做是同樣的. 命令行程序和Web程序相似, 命令行參數傳遞給要執行的腳本,至關於經過url 請求一個PHP頁面. 腳本戳裏完成後返回響應結果,只不過命令行響應的結果是顯示在終端上. 腳本執行的開始都是經過SAPI接口進行的. apache

        1)、啓動apache:當給定的SAPI啓動時,例如在對/usr/local/apache/bin/apachectl start的響應中,PHP由初始化其內核子系統開始。在接近啓動例程的末尾,它加載每一個擴展的代碼並調用其模塊初始化例程(MINIT)。這使得每一個擴展能夠初始化內部變量、分配資源、註冊資源處理器,以及向ZE註冊本身的函數,以便於腳本調用這其中的函數時候ZE知道執行哪些代碼。編程

       2)、請求處理初始化:接下來,PHP等待SAPI層請求要處理的頁面。對於CGI或CLI等SAPI,這將馬上發生且只發生一次。對於Apache、IIS或其餘成熟的web服務器SAPI,每次遠程用戶請求頁面時都將發生,所以重複不少次,也可能併發。無論請求如何產生,PHP開始於要求ZE創建腳本的運行環境,而後調用每一個擴展的請求初始化 (RINIT)函數。RINIT使得擴展有機會設定特定的環境變量,根據請求分配資源,或者執行其餘任務,如審覈。 session擴展中有個RINIT做用的典型示例,若是啓用了session.auto_start選項,RINIT將自動觸發用戶空間的session_start()函數以及預組裝$_SESSION變量。api

      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層,最終輸出到瀏覽器上。

三、PHP的開始和結束階段

開始階段有兩個過程:

     第一個過程:apache啓動的過程,即在任何請求到達以前就發生。是在整個SAPI生命週期內(例如Apache啓動之後的整個生命週期內或者命令行程序整個執行過程當中)的開始階段(MINIT),該階段只進行一次.。啓動Apache後,PHP解釋程序也隨之啓動; PHP調用各個擴展(模塊)的MINIT方法,從而使這些擴展切換到可用狀態。看看php.ini文件裏打開了哪些擴展吧; MINIT的意思是「模塊初始化」。各個模塊都定義了一組函數、類庫等用以處理其餘請求。 模塊在這個階段能夠進行一些初始化工做,例如註冊常量, 定義模塊使用的類等等.典型的的模塊回調函數MINIT方法以下:

 

 

  1. PHP_MINIT_FUNCTION(myphpextension) { /* Initialize functions, classes etc */ }  
  2. {  
  3.     // 註冊常量或者類等初始化操做   
  4.     return SUCCESS;   
  5. }  

     第二個過程發生在請求階段,當一個頁面請求發生時.則在每次請求以前都會進行初始化過程(RINIT請求開始).

請求到達以後,SAPI層將控制權交給PHP層,PHP初始化本次請求執行腳本所需的環境變量,例如建立一個執行環境,包括保存php運行過程當中變量名稱和變量值內容的符號表. 以及當前全部的函數以及類等信息的符號表.例如是Session模塊的RINIT,若是在php.ini中啓用了Session 模塊,那在調用該模塊的RINIT時就會初始化$_SESSION變量,並將相關內容讀入;  而後PHP會調用全部模塊RINIT函數,即「請求初始化」。 在這個階段各個模塊也能夠執行一些相關的操做, 模塊的RINIT函數和MINIT函數相似 ,RINIT方法能夠看做是一個準備過程,在程序執行之間就會自動啓動。

 

  1. PHP_RINIT_FUNCTION(myphpextension)  
  2. {  
  3.     // 例如記錄請求開始時間   
  4.     // 隨後在請求結束的時候記錄結束時間.這樣咱們就可以記錄下處理請求所花費的時間了  
  5.     return SUCCESS;   
  6. }  

結束階段分爲兩個環節:

請求處理完後就進入告終束階段, 通常腳本執行到末尾或者經過調用exit()或者die()函數,PHP都將進入結束階段. 和開始階段對應,結束階段也分爲兩個環節,一個在請求結束後(RSHUWDOWN),一個在SAPI生命週期結束時(MSHUTDOWN).
 
第一個環節:請求處理完後結束階段:請求處理完後就進入告終束階段,PHP就會啓動清理程序。它會按順序調用各個模塊的RSHUTDOWN方法。 RSHUTDOWN用以清除程序運行時產生的符號表,也就是對每一個變量調用unset函數。典型的RSHUTDOWN方法以下:
  1. PHP_RSHUTDOWN_FUNCTION(myphpextension)  
  2. {  
  3.     // 例如記錄請求結束時間, 並把相應的信息寫入到日至文件中.  
  4.     return SUCCESS;   
  5. }  

第二個環節:最後,全部的請求都已處理完畢,SAPI也準備關閉了, PHP調用每一個擴展的MSHUTDOWN方法,這是各個模塊最後一次釋放內存的機會。(這個是對於CGI和CLI等SAPI,沒有「下一個請求」,因此SAPI馬上開始關閉。)

典型的RSHUTDOWN方法以下:

 

[plain]  view plain copy print ?
  1. PHP_MSHUTDOWN_FUNCTION(extension_name) {   
  2.     /* Free handlers and persistent memory etc */   
  3.     return SUCCESS;   
  4. }  

 

這樣,整個PHP生命週期就結束了。要注意的是,只有在服務器沒有請求的狀況下才會執行「啓動第一步」和「關閉第二步」。

SAPI運行PHP都通過下面幾個階段:
       一、模塊初始化階段(Module init)     :
           即調用每一個拓展源碼中的的PHP_MINIT_FUNCTION中的方法初始化模塊,進行一些模塊所需變量的申請,內存分配等。
        二、請求初始化階段(Request init)  :
           即接受到客戶端的請求後調用每一個拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP腳本的執行環境。
        三、執行PHP腳本
        四、請求結束(Request Shutdown) 
          這時候調用每一個拓展的PHP_RSHUTDOWN_FUNCTION方法清理請求現場,而且ZE開始回收變量和內存。
        五、關閉模塊(Module shutdown)     :
           Web服務器退出或者命令行腳本執行完畢退出會調用拓展源碼中的PHP_MSHUTDOWN_FUNCTION 方法

 

四、單進程SAPI生命週期

 

CLI/CGI模式的PHP屬於單進程的SAPI模式。這類的請求在處理一次請求後就關閉。也就是隻會通過以下幾個環節: 開始 - 請求開始 - 請求關閉 - 結束 SAPI接口實現就完成了其生命週期。如圖所示:

                                      

 

 

五、多進程SAPI生命週期

 

一般PHP是編譯爲apache的一個模塊來處理PHP請求。Apache通常會採用多進程模式, Apache啓動後會

fork出多個子進程,每一個進程的內存空間獨立,每一個子進程都會通過開始和結束環節, 不過每一個進程的開始階

段只在進程fork出來以來後進行,在整個進程的生命週期內可能會處理多個請求。 只有在Apache關閉或者進程

被結束以後纔會進行關閉階段,在這兩個階段之間會隨着每一個請求重複請求開始-請求關閉的環節。 

如圖所示:

                                     

 

六、多線程的SAPI生命週期

多線程模式和多進程中的某個進程相似,不一樣的是在整個進程的生命週期內會並行的重複着 請求開始-請求關閉的環節.

 

 

在這種模式下,只有一個服務器進程在運行着,但會同時運行不少線程,這樣能夠減小一些資源開銷,向Module init和Module shutdown就只須要運行一遍就好了,一些全局變量也只須要初始化一次,由於線程獨具的特質,使得各個請求之間方便的共享一些數據成爲可能。

 多線程工做方式以下圖

                              

 

七、Apache通常使用多進程模式prefork

        在linux下使用#http –l 命令能夠查看當前使用的工做模式。也可使用#apachectl -l命令。
        看到的prefork.c,說明使用的prefork工做模式。

        prefork 進程池模型,用在 UNIX 和相似的系統上比較多,主要是因爲寫起來方便,也容易移植,還不容易出問題。要知道,若是採用線程模型的話,用戶線程、內核線程和混合型線程有不一樣的特性,移植起來就麻煩。prefork 模型,即預先 fork() 出來一些子進程緩衝一下,用一個鎖來控制同步,鏈接到來了就放行一個子進程,讓它去處理。

 

prefork MPM 使用多個子進程,每一個子進程只有一個線程。每一個進程在某個肯定的時間只能維持一個鏈接。在大多數平臺上,Prefork MPM在效率上要比Worker MPM要高,可是內存使用大得多。prefork的無線程設計在某些狀況下將比worker更有優點:他可以使用那些沒有處理好線程安全的第三方模塊,並 且對於那些線程調試困難的平臺而言,他也更容易調試一些。
相關文章
相關標籤/搜索