php的擴展和嵌入–php的生命周期與變量詳述

首先開始介紹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;
                    }

                    • 在非線程安全的情況下,隻需要簡單的聲明變量,更加快速有效,數據的地址在編譯階段就可以確定,而不是像在線程安全的情況下需要運行時計算。同時它還有一個好處就是一段程式出瞭bug不會讓整個webserver壞掉。因此在不需要用到線程安全的時候,還是應該盡量使用非線程安全聲明方式。
                    • 為瞭構建線程安全的php,必須在編譯的時候加上–enable-maintainer-zts選項。而在程式中檢測的時候可以用#ifdef ZTS,通過這種檢測可以實現對全局變量聲明的不同處理,入下例所示:

                    • #ifdef ZTS
                      #define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
                      #else
                      #define HELLO_G(v) (hello_globals.v)
                      #endif

                      • 通過判斷是否啟動瞭線程安全,對變量進行瞭不同的聲明和訪問方式指定。
                      • 在啟用瞭線程安全之後,為瞭在不同的線程之間對數據進行區分,PHP會自動啟用一個tsrm_ls指針。在這個指針的幫助下,各個線程的函數才能找到對應的符號表進行相應的變量讀取操作。也正是這種機制使得多個線程一起工作的時候,數據空間不會亂掉。

                        通過對php的起始和結束階段、生命周期和Zend線程安全的機制的瞭解,有利於後續對php擴展編譯的研究。

You May Also Like