mod_php模式原理探析

1、PHP與Apache工作模式

在傳統的LAMP架構中,PHP與Apache交互時,至少有兩種方式『運行PHP』:

  • 使用CGI:Apache發送請求至php-cgi進程,php-cgi進程調用PHP解釋器,然後由PHP解釋器進程解釋php腳本代碼。
  • 使用mod_php作爲Apache的一個模塊:PHP解釋器作爲Apache的一個內置模塊,即不存在外部的PHP進程,而是由Apache(中的mod_php模塊)進程解釋執行PHP腳本 - 這意味着PHP與Apache通信更方便快捷。

其中,『運行PHP』是指調用PHP解釋器解釋執行PHP腳本。

通過php的'php_sapi_name()’函數可知道,當前系統採用哪種工作模式。如當值爲’apache2handler’時即表示:mod_php模式。

2、Apache模塊加載原理

Apache的模塊可以以靜態方式編譯到可執行程序中,也可以在Apache運行過程中動態加載(以動態鏈接庫的方式)。這意味着:可以對Apache服務器程序進行擴展而無需重新源碼編譯它,甚至無需重啓它。
所需要做的就是:向服務器發送HUP或者AP_SIG_GRACEFUL信號,通知服務器重新加載模塊。
關於向Apache發送HUP信號:

'Sending the HUP or restart signal to the parent causes it to kill off its children like in TERM, but the parent doesn't exit. It re-reads its configuration files, and re-opens any log files. Then it spawns a new set of children and continues serving hits.

回到mod_php模塊,Apache動態加載模塊的過程:

  • 首先,在Apache配置文件http.conf中增加:LoadModule php7_module libexec/apache2/libphp7.so,表示運行過程中加載PHP模塊的動態鏈接庫文件:libphp7.so。
  • 然後,通過Apache的內部函數(以Hook的方式)獲取動態鏈接庫的內容,並將PHP模塊的內容加載到內存中指定的變量中。

其中PHP7源碼中,PHP模塊(php7_module)的數據結構爲:

AP_MODULE_DECLARE_DATA module php7_module = {
     STANDARD20_MODULE_STUFF, /*宏,包括了module結構的前8個字段:版本號、小版本號、模塊索引、模塊名、當前模塊指針、下一個動態加載的模塊指針、魔數、rewrite_args函數指針*/
     create_php_config,          /* create per-directory config structure */
     merge_php_config,          /* merge per-directory config structures */
     NULL,                         /* create per-server config structure */
     NULL,                         /* merge per-server config structures */
     php_dir_cmds,               /* command apr_table_t */
     php_ap2_register_hook     /* register hooks */
};

其中,php_ap2_register_hook是一系列的hook調用:

void php_ap2_register_hook(apr_pool_t *p)
{
     ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
#ifdef ZEND_SIGNALS
     ap_hook_child_init(zend_signal_init, NULL, NULL, APR_HOOK_MIDDLE);
#endif
     ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}
  • pre_config、post_config、child_init是啓動時執行的鉤子,它們會在Apache服務器啓動時調用。其中,在post_config鉤子中啓動PHP解釋器模塊(由php_apache_server_startup函數實現:'通過調用sapi_startup啓動sapi, 並通過調用php_apache2_startup來註冊sapi module struct(此結構在本節開頭中有說明), 最後調用php_module_startup來初始化PHP, 其中又會初始化ZEND引擎,以及填充zend_module_struct中 的treat_data成員(通過php_startup_sapi_content_types)等')。
  • handler是請求時執行的鉤子,它會在Apache服務器處理請求時調用。

3、Apache Hooking機制:

在Apache2.4中如果需要處理請求時,你只需要創建一個鉤子(Hook),掛於請求處理程序上。
一個鉤子,本質上是一條信息:告訴服務器它要麼服務用戶發起的請求要麼只是瞥一眼該請求。Apache所有的模塊(包括mod_rewrite, mod_authn_*, mod_proxy等)均是將鉤子掛於請求程序的各個部分來實現的 - are hooked into specific parts of the request process。

modules serve different purposes; Some are authentication/authorization handlers, others are file or script handlers while some third modules rewrite URIs or proxies content.

Apache服務器本身無需知道每個模塊具體負責處理哪個部分以及處理什麼,它只需要:在客戶端請求達到的時候詢問下哪個模塊對這個請求『感興趣』即可,而每個模塊只需選擇要還是不要,如果要那按照鉤子定義的內容處理然後返回接口。

圖片描述

圖片來源於Apache官網

Apache允許外部模塊可以將自定義的函數注入到自己的請求處理循環中,從而參與Apache的請求處理過程。
通過Hook機制,PHP模塊可以在Apache請求處理流程中負責處理那些關於php腳本的請求(即負責解釋、執行php腳本)。
具體實現方式可以詳見在PHP源碼中實現Apahce的ap_hook_post_config鉤子:PHP以模塊方式註冊到Apache的掛鉤上去。這樣在Apache進程運行時一有php請求,就可以加載動態鏈接庫(libphp7.so文件)形式的PHP模塊,用來處理php請求。

REFERENCES

1、http://stackoverflow.com/ques...
2、http://www.phppan.com/2011/01...
3、https://github.com/php/php-sr...
4、https://github.com/php/php-sr...
5、https://httpd.apache.org/docs...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章