首先開始介紹php的生命周期,了解一個php程序從開始運行到最後結束究竟經過怎麼樣的過程,對學習php和平時php開發應該是很重要的。
起始和關閉階段:
- 對於php的起始和關閉階段可以分成兩層,
- 第一層是php解釋器作為一個整體進行結構和值的初始化過程。
- 第二層則是在每一個頁面的請求過程中。
- 對於每個擴展而言,都會有一個初始化MINT函數,這個過程會聲明變量、類,注冊資源、流和過濾處理器,這些操作在所有的請求中都是存在的,所以可以稱為是Persistent的。一般進行如下的兩步操作:
-
REGISTER_INI_ENTRIES()
-
初始化模塊的全局變量
-
而在頁面發出請求了之後,PHP則會建立一個包括符號表和配置值在內的操作環境,然後這次php解釋器會再次循環每一個擴展,調用每個擴展的RINIT初始化函數。一般RINT函數的操作如下:
-
把globalvalue設成默認值,這些全局變量往往是每個請求都需要的,但是針對每一個請求而言都是相互獨立的。
-
那些需要用到的變量需要放到符號表中以備調用。
-
在這個函數中還可以記錄一下請求的相關信息
-
完成了請求的處理之後,如果到達了腳本尾部或通過die() exit()推出了,那麼php通過調用RSHUTDOWN()開始清理
- 這時候每個符號表中的變量都會被unset掉
- 而當所有的請求都被滿足了之後,就開始針對於模塊的MSHUTDOWN過程
-
調用UNREGISTER_INI_ENTRYES(),與MINIT函數的初始化過程相互對應。
php程序執行的生命周期:
要了解生命周期,就必須對不同的執行方式有所涉及。php可以有幾種不同的執行方式,每種方式都有其特定的生命周期。
- CLI:這個是從命令行執行php程序,它的生命周期最為簡單。比如在執行test.php的時候,就經歷了如下的過程
-
- 圖1 CLI進行php執行的過程
- 注意到MINIT RINIT RSHUTDOWN MSHUTDOWN都只被調用過一次,這個比較類似於瀑布式的結構。
- 多線程的模塊方式:這是最常用的一種方式,php作為APXS的模塊對apache進行配合。在apache啟動的時候會fork很多的子進程。針對於多個不同的請求,配合的是多個不同的初始化與結束過程。但是對每一個線程來說,只有一個MINIT和MSHUTDOWN的調用。而每個請求都對應著自己單獨的RINIT和RSHUTDOWN。
- 多線程的模塊方式:采用多線程的方法可以避免不同的線程重復的調用MINIT/MSHUTDOWN.它具有的好處是多個請求可以共享信息,但是請求之間的隔離要求比較高,不然容易出現變量的訪問出錯。
Zend 線程安全
php對線程安全的處理有專門的機制,稱為Thread Safe Resource Management(TSRM)。在進行線程安全和非線程安全聲明的時候,明顯有一些不同之處:
- 線程安全的變量聲明:
-
typedef struct {
int sampleint;
char *samplestring;
} php_sample_globals;
int sample_globals_id;
PHP_MINIT_FUNCTION(sample)
{
ts_allocate_id(&sample_globals_id,
sizeof(php_sample_globals),
(ts_allocate_ctor) php_sample_globals_ctor,
(ts_allocate_dtor) php_sample_globals_dtor);
return SUCCESS;
}
- 從這段代碼中可以看到,在MINIT階段,需要通過ts_allocate_id函數來通知TSRM這個程序需要多少空間,TSRM會增加當前的空間消耗,並返回一個id指向線程數據池的相應部分。
-
而當請求要訪問數據的時候,就首先從TSRM層找到當前線程的資源池的指針,然後加上ts_allocate_id()返回的資源id作為offset。
- 非線程安全的變量聲明:
-
typedef struct {
int sampleint;
char *samplestring;
} php_sample_globals;
php_sample_globals sample_globals;
PHP_MINIT_FUNCTION(sample)
{
php_sample_globals_ctor(&sample_globals TSRMLS_CC);
return SUCCESS;
}