PHP7內核剖析讀書筆記-SAPI


SAPI是PHP框架的接口層,是進入PHP內部的入口。
典型的SAPI有3個:Cli,Fpm,Embed。
此篇主要總結Cli和Fpm。

CLI

CLI----Command Line Interface ,命令行接口用於在命令行下執行PHP腳本。
Cli是單進程的,處理完請求就直接關閉了,生命週期先後經歷了模塊開始(module_startup),請求開始(request_stratup),解析腳本(execute script),關閉請求(request shutdown),關閉模塊(module shutdown)。
關鍵的處理過程。

main() -> php_cli_startup() -> do_cli() -> php_module_shutdown()

1.main函數執行時首先解析命令行參數,初始化記錄SAPI信息的結構體(sapi_module_struct)。
2.完成初始化之後進入module startup階段。module startup之後進入請求初始化,do_cli()完成請求的處理。
3.PHP腳本執行的時候輸入形式有很多種,比如文件路徑,文件句柄,文件描述符等等。cli中用的是文件句柄。定義好請求的輸入結構後將進行請求初始化操作(request startup),執行PHP腳本。
4.完成腳本後進入request shutdown。
5.do_cli()執行完成後回到main()函數中,進入module shutdown,最後進程退出。

特點:單進程。在執行php -m等查詢系統信息的請求時不需要經歷PHP請求的生命週期。

FPM

FPM----FastCGI Process Manager 是PHP FastCGI運行模式的一個進程管理器。
PHP實現了FastCGI協議的處理,但是沒有實現具體的網絡處理。兩種常用的網絡模型。

  • 多進程模型:一個主進城和多個子進程組成,主進城負責管理子進程。例如nginx。
  • 多線程模型:這種模型通常會由主線程監聽,接受請求,子線程處理。例如memcached。
  • 注意:進城擁有獨立的地址空間和資源,線程共享進城的地址空間和資源。所以多線程要考慮線程安全,資源衝突的問題。

FPM的基本實現

 1.Fpm是一個多進程的模型,是由一個master和多個worker進程組成的。master進程啓動的時候創建socket,由fork出的子進程接受並且處理請求。
 2.master主要作用是管理worker進城,負責fork或者kill掉worker進城。   
 3.worker進程的工作原理的爲搶佔式的接受請求,接受成功後解析FastCGI,然後執行相應的腳本,處理完畢關閉請求,等待新的連接。所以worker爲阻塞式的,一個沒有處理完不會去處理下一個請求。這種處理方式不需要考慮併發導致的資源衝突。
 4.master和worker不會直接進行通信,master通過共享內存獲取worker進程的信息。

worker請求處理

worker進程返回main()函數後繼續向下執行,此後的流程就是worker進程不斷接受請求,有請求到達後讀取並解析FastCGI協議的數據,解析完成後執行PHP腳本,執行完畢後關閉請求。
worker處理請求生命週期。
1.等待請求。worker進程阻塞在fcgi_accept_request()中等待請求。
2.解析請求。fastcgi請求到達後被worker接受,然後接受並解析請求數據。
3.請求初始化。執行php_request_startup(),此階段會調用每個擴展的鉤子函數。
4.執行PHP腳本。php_execute_script()完成的PHP腳本的編譯,執行。
5.關閉請求。執行php_request_shutdown()。
在這裏插入圖片描述

master進程管理

fpm的三種進程管理方式。
  • 靜態模式(static):在啓動時master根據pm.max_children配置fork出相應數量的worker進程,此種狀態下worker進程數目是固定不變的。
  • 動態模式(dynamic):在Fpm啓動時會根據pm.start_servers配置初始化一定數量的worker。運行期間如果master發現空閒worker數低於pm.min_spare_servers配置數,表示請求較多,worker不夠,此時會fork worker進程,但是總數不能超過最大worker數目(pm.max_children)。如果master發現空閒worker數據超過了pm.max_spare_servers,則會殺掉一些worker。
  • 按需模式(ondemand):這種模式在啓動時不分配worker進程,等到有請求後再通知master進程fork worker子進程。

master在調用fpm_run()後不再返回,會進入fpm_event_loop()事件循環,在這個方法中master將循環處理master註冊的幾個I/O及定時器。

void fmp_event_loop(int err){
   //註冊I/O,定時器時間
   ....
   //進入事件循環,master阻塞在此
   while(1){
   	 ...
   	 //等待I/O事件
   	 ret = module->wait(fpm_event_queue_fd, timeout);
   	 ...
   	 //檢查定時器事件
   	 ...
	}
}
具體master註冊的重要事件
  1. 信號事件
static struct fmp_event_s signal_fd_event;
//設置監聽的fd,事件觸發類型及回調函數
fmp_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fmp_got_signal, NULL);
//註冊事件
fpm_event_add(&signal_fd_event, 0);

fpm_init()階段時分配了一個sp管道,當master收到SIGTERM,SIGINT,SIGUSR1,SIGUSR2,SIGCHLD,SIGQUIT這些信號時會把信號寫到sp[1]管道中。
fpm_signals_get_fd()返回的是sp[0]。當sp[0]可讀時回調fmp_got_signal()進行處理。
當向master發送信號時,首先handler會把信號通知發送到sp[1]管道,觸發sp[0]可讀事件,回調fpm_got_signal()進行處理。
在這裏插入圖片描述

  1. 進程檢查定時器

fpm_event_loop()中調用fpm_pctl_perform_idle_server_maintenance_heartbeat()註冊了一個定時器:
這個定時器是用來定期檢查worker進程數的,master通過這個定時器每隔一段時間檢測worker數量。根據之前三種策略決定是否fork或者kill。

  1. 執行超時檢查定時器

php-fpm.conf中有一個request_terminate_timeout的配置項,如果worker處理一個請求的總時長超過此值,那麼master會向此worker進程發送kill -TERM信號殺掉worker進程。
這個功能通過定時器實現的,master每隔一定時間檢查所有處理中的worker,如果發現其處理時間達到閾值則殺掉這個worker。另外FPM記錄的slow log也是通過這個定時器完成的。

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