了讓Apache支持php,我們通常的做法是編譯一個apche的php模塊, 在配置中配置讓mod_php來處理php文件的請求. php模塊通過注冊apache2的ap_hook_post_config掛鉤, 在apache啟動的時候啟動php模塊以接受php的請求.
下面介紹apache模塊加載的基本知識以及PHP對于apache的實現
Apache模塊加載機制簡介
Apache的模塊可以在運行的時候動態裝載,這意味著對服務器可以進行功能擴展而不需要重新對源代碼進行編譯,甚至根本不需要停止服務器。 我們所需要做的僅僅是給服務器發送信號HUP或者AP_SIG_GRACEFUL通知服務器重新載入模塊。 但是在動態加載之前,我們需要將模塊編譯成為動態鏈接庫。此時的動態加載就是加載動態鏈接庫。
Apache中對動態鏈接庫的處理是通過模塊mod_so來完成的,因此mod_so模塊不能被動態加載, 它只能被靜態編譯進Apache的核心。這意味著它是隨著Apache一起啟動的。
比如我們要加載PHP模塊,那么首先我們需要在httpd.conf文件中添加一行:
LoadModule php5_module modules/mod_php5.so
該命令的第一個參數是模塊的名稱,名稱可以在模塊實現的源碼中找到。第二個選項是該模塊所處的路徑。 如果需要在服務器運行時加載模塊,可以通過發送信號HUP或者AP_SIG_GRACEFUL給服務器,一旦接受到該信號,Apache將重新裝載模塊,而不需要重新啟動服務器。
下面我們以PHP模塊的加載為例,分析Apache的模塊加載過程。在配置文件中添加了所上所示的指令后,Apache在加載模塊時會根據模塊名查找模塊并加載, 對于每一個模塊,Apache必須保證其文件名是以“mod_”開始的,如php的mod_php5.c。如果命名格式不對,Apache將認為此模塊不合法。 module結構的name屬性在最后是通過宏STANDARD20_MODULE_STUFF以__FILE__體現。 關于這點可以在后面介紹mod_php5模塊時有看到。 通過之前指令中指定的路徑找到相關的動態鏈接庫文件,Apache通過內部的函數獲取動態鏈接庫中的內容,并將模塊的內容加載到內存中的指定變量中。
在真正激活模塊之前,Apache會檢查所加載的模塊是否為真正的Apache模塊,這個檢測是通過檢查magic字段進行的。而magic字段是通過宏STANDARD20_MODULE_STUFF體現,在這個宏中magic的值為MODULE_MAGIC_COOKIE,MODULE_MAGIC_COOKIE定義如下:
#define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */
最后Apache會調用相關函數(ap_add_loaded_module)將模塊激活,此處的激活就是將模塊放入相應的鏈表中(ap_top_modules鏈表:ap_top_modules鏈表用來保存Apache中所有的被激活的模塊,包括默認的激活模塊和激活的第三方模塊。)
Apache2的mod_php5模塊說明
Apache2的mod_php5模塊包括sapi/apache2handler和sapi/apache2filter兩個目錄 在apache2_handle/mod_php5.c文件中,模塊定義的相關代碼如下:
AP_MODULE_DECLARE_DATA module php5_module={ STANDARD20_MODULE_STUFF, /* 宏,包括版本,小版本,模塊索引,模塊名,下一個模塊指針等信息,其中模塊名以__FILE__體現 */ 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, /* 模塊定義的所有的指令 */ php_ap2_register_hook /* 注冊鉤子,此函數通過ap_hoo_開頭的函數在一次請求處理過程中對于指定的步驟注冊鉤子 */};
它所對應的是apache的module結構,module的結構定義如下:
typedef struct module_struct module;struct module_struct { int version; int minor_version; int module_index; const char *name; void *dynamic_load_handle; struct module_struct *next; unsigned long magic; void (*rewrite_args) (process_rec *process); void *(*create_dir_config) (apr_pool_t *p, char *dir); void *(*merge_dir_config) (apr_pool_t *p, void *base_conf, void *new_conf); void *(*create_server_config) (apr_pool_t *p, server_rec *s); void *(*merge_server_config) (apr_pool_t *p, void *base_conf, void *new_conf); const command_rec *cmds; void (*register_hooks) (apr_pool_t *p);}
上面的模塊結構與我們在mod_php5.c中所看到的結構有一點不同,這是由于STANDARD20_MODULE_STUFF的原因,這個宏它包含了前面8個字段的定義。 STANDARD20_MODULE_STUFF宏的定義如下:
/** Use this in all standard modules */#define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \ MODULE_MAGIC_NUMBER_MINOR, \ -1, \ __FILE__, \ NULL, \ NULL, \ MODULE_MAGIC_COOKIE, \ NULL /* rewrite args spot */
php_dir_cmds所定義的內容如下:
const command_rec php_dir_cmds[]={ AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, OR_OPTIONS, "PHP Value Modifier"), AP_INIT_TAKE2("php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, "PHP Flag Modifier"), AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"), AP_INIT_TAKE2("php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"), AP_INIT_TAKE1("PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, "Directory containing the php.ini file"), {NULL}};
以上為php模塊定義的指令表。它實際上是一個command_rec結構的數組。 當Apache遇到指令的時候將逐一遍歷各個模塊中的指令表,查找是否有哪個模塊能夠處理該指令, 如果找到,則調用相應的處理函數,如果所有指令表中的模塊都不能處理該指令,那么將報錯。 如上可見,php模塊僅提供php_value等5個指令。
php_ap2_register_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); ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);}
以上代碼聲明了pre_config,post_config,handler和child_init 4個掛鉤以及對應的處理函數。 其中pre_config,post_config,child_init是啟動掛鉤,它們在服務器啟動時調用。 handler掛鉤是請求掛鉤,它在服務器處理請求時調用。其中在post_config掛鉤中啟動php。 它通過php_apache_server_startup函數實現。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)等。
Apache的運行過程
Apache的運行分為啟動階段和運行階段。 在啟動階段,Apache為了獲得系統資源最大的使用權限,將以特權用戶root(*nix系統)或超級管理員Administrator(Windows系統)完成啟動,并且整個過程處于一個單進程單線程的環境中,。 這個階段包括配置文件解析(如http.conf文件)、模塊加載(如mod_php,mod_perl)和系統資源初始化(例如日志文件、共享內存段、數據庫連接等)等工作。
Apache的啟動階段執行了大量的初始化操作,并且將許多比較慢或者花費比較高的操作都集中在這個階段完成,以減少了后面處理請求服務的壓力。
在運行階段,Apache主要工作是處理用戶的服務請求。 在這個階段,Apache放棄特權用戶級別,使用普通權限,這主要是基于安全性的考慮,防止由于代碼的缺陷引起的安全漏洞。 Apache對HTTP的請求可以分為連接、處理和斷開連接三個大的階段。同時也可以分為11個小的階段,依次為: Post-Read-Request,URI Translation,Header Parsing,Access Control,Authentication,Authorization,MIME Type Checking,FixUp,Response,Logging,CleanUp
Apache Hook機制
Apache 的Hook機制是指:Apache 允許模塊(包括內部模塊和外部模塊,例如mod_php5.so,mod_perl.so等)將自定義的函數注入到請求處理循環中。換句話說,模塊可以在 Apache的任何一個處理階段中掛接(Hook)上自己的處理函數,從而參與Apache的請求處理過程。 mod_php5.so/ php5apache2.dll就是將所包含的自定義函數,通過Hook機制注入到Apache中,在Apache處理流程的各個階段負責處理php請求。 關于Hook機制在Windows系統開發也經常遇到,在Windows開發既有系統級的鉤子,又有應用級的鉤子。
以上介紹了apache的加載機制,hook機制,apache的運行過程以及php5模塊的相關知識,下面簡單的說明在查看源碼中的一些常用對象。
Apache常用對象
在說到Apache的常用對象時,我們不得不先說下httpd.h文件。httpd.h文件包含了Apache的所有模塊都需要的核心API。 它定義了許多系統常量。但是更重要的是它包含了下面一些對象的定義。
request_rec對象
當一個客戶端請求到達Apache時,就會創建一個request_rec對象,當Apache處理完一個請求后,與這個請求對應的request_rec對象也會隨之被釋放。 request_rec對象包括與一個HTTP請求相關的所有數據,并且還包含一些Apache自己要用到的狀態和客戶端的內部字段。
server_rec對象
server_rec定義了一個邏輯上的WEB服務器。如果有定義虛擬主機,每一個虛擬主機擁有自己的server_rec對象。 server_rec對象在Apache啟動時創建,當整個httpd關閉時才會被釋放。 它包括服務器名稱,連接信息,日志信息,針對服務器的配置,事務處理相關信息等 server_rec對象是繼request_rec對象之后第二重要的對象。
conn_rec對象
conn_rec對象是TCP連接在Apache的內部體現。 它在客戶端連接到服務器時創建,在連接斷開時釋放。
PHP高效、穩定的運行依賴于服務器的編譯和執行,本節主要介紹如何在微軟公司的 Windows操作系統中架設安全、可靠的PHP運行環
與普通的HTML文件不同,PHP文件的執行依賴于服務器的編譯,很多流行的服務器都支 持PHP腳本的編譯,例如US、Apache。本例主要介紹如何在Windows操作系統中安裝和配置 Apache服務。安裝Apache前,應到官方網站http://www.apache.org下載Apache的安裝程序。 L實現過程?
(1)下載 Apache 的安裝包"Apache_2.0.59-win32-x86-no_ssl.msi” 后,雙擊該安裝包,就 可以彈出圖L1所示的Apache的安裝對?話?框。
(2)在圖L1所示的對話框中,單擊“Next”按鈕,彈出圖1.2所示的Apache許可協議對 話框,選擇圖中的UI accept the terms in the license agreementw單選按鈕,然后單擊“Next”按 鈕,彈出圖L3所示的輸入服務器信息對話框。
??
??
圖1.1 Apache的安裝對話框
??
??
圖1.2許可協議對話框
??
??
圖L3輸入服務器信息對話框
(3)在圖1.3所示的對話框中需要輸入服務器的相關信息,網絡域名、計算機名和管理員 郵箱等,可以根據用戶的實際情況輸入。在該對話框下方的單選按鈕組中,如果選擇第一項, 可以對任何用戶開放Apache服務,同時設置服務器的偵聽端口為“80”,如果選擇第二項,則 只有本地用戶可以連接和使用Apache服務。
設置完成后,單擊圖1.3中的“Next”按鈕,彈出圖14所示的選擇安裝方式對?話?框。
(4)在圖1.4所示的對話框中有兩種選擇方式:“Typical”典型安裝和“Custom”自定 ?義?安裝。通常,用戶都選擇典型安裝方式,單擊“Next”按鈕,彈出圖1.5所示的安裝對?話?框。
??
??
圖1.4選擇安裝方式
??
??
圖L5設置安裝的路徑
??
??
(5)在圖1.5所示的設置安裝的路徑對話框中,可以選擇安裝的路徑。單擊“Change”按 鈕,彈出圖1.6所示的對話框來修改軟件的安裝路徑。
(6)在圖L6所示的對話框中,對安裝的路徑進行設置,然后單擊“OK”按鈕,彈出圖1.7 所示準備安裝的對話框。
(7)在圖1.7所示的安裝對話框中,單擊“Install”按鈕,打開圖1.8所示的對話框并開始 安裝。
(8)中所有文件復制完成后,Apache安裝完成,彈出圖1.9所示的對?話?框,單擊“Finish” 按鈕完成Apache安裝。
(9)接下來,測試Apache服務是否安裝成功。選擇“開始”一“所有程序”選項,在彈 出的菜單中能夠看到Apache服務器相關操作列表,同時如果在系統托盤中有一個13圖標,
??
??
??
??
??
??
圖1.11 Apache服務器的監視器
器地址欄中輸入llhttp://localhost/"或者輸入uhttp://127.0.0.1/",如果能夠看到如圖1.12所示的 窗口,則說明Apache安裝成功。