PHP核心剖析之fpm(未完待續)

2021-08-22 10:40:14 字數 4782 閱讀 2778

fpm(fastcgi process manager)是php fastcgi執行模式的乙個程序管理器,從它的定義可以看出,fpm的核心功能是程序管理,那麼它用來管理什麼程序呢?這個問題就需要從fastcgi說起了。

fastcgi是web伺服器(如:nginx、apache)和處理程式之間的一種通訊協議,它是與http類似的一種應用層通訊協議,注意:它只是一種協議!

php只是乙個指令碼解析器,你可以把它理解為乙個普通的函式,輸入是php指令碼。輸出是執行結果,假如我們想用php代替shell,在命令列中執行乙個檔案,那麼就可以寫乙個程式來嵌入php解析器,這就是cli模式,這種模式下php就是普通的乙個命令工具。接著我們又想:能不能讓php處理http請求呢?這時就涉及到了網路處理,php需要接收請求、解析協議,然後處理完成返回請求。在網路應用場景下,php並沒有像golang那樣實現http網路庫,而是實現了fastcgi協議,然後與web伺服器配合實現了http的處理,web伺服器來處理http請求,然後將解析的結果再通過fastcgi協議**給處理程式,處理程式處理完成後將結果返回給web伺服器,web伺服器再返回給使用者

php實現了fastcgi協議的解析,但是並沒有具體實現網路處理,常用的網路模型有以下兩種

多程序模型:多程序模型通常是主程序只負責管理子程序,而基本的網路事件由各個子程序處理,nginx、fpm就是這種模式

多執行緒模型:多執行緒模型與多程序類似,只是它是執行緒粒度,通常會由主線程監聽、接收請求,然後交由子執行緒處理,memcached就是這種模式,有的也是採用多程序那種模式:主線程只負責管理子執行緒不處理網路事件,各個子執行緒監聽、接收、處理請求,memcached使用udp協議時採用的是這種模式。

master程序的作用是什麼?

fpm是一種多程序的模型,由乙個master程序和若干worker程序組成(具體數量需要看php-fpm.conf的配置)master啟動的時候回建立乙個socket,但是不會接受,處理請求,而是fork出worker子程序去接受和處理請求

所以master程序的主要作用就是管理worker程序,負責fork或者kill掉子程序,當請求多worker處理不過來了這是master會fork新的worker程序處理,如果空閒的程序多的時候,就會kill掉一些worker程序,避免占用浪費系統資源(像乙個工頭,他不幹活,監督大家搬磚,活多了找幾個人來幹活,活兒少了,空閒的工人多了就開除幾個)

worker程序的作用是什麼?

worker程序的主要工作是處理請求,每個worker程序都會**競爭地**accept請求,接受成功後會解析fastcgi,然後執行指令碼,完成後關閉請求,繼續等待新的連線,這是worker程序的宣告週期。

概括來說,fpm的實現就是建立乙個master程序,在master程序中建立並監聽socket,然後fork出多個子程序,這些子程序各自accept請求,子程序的處理非常簡單,它在啟動後阻塞在accept上,有請求到達後開始讀取請求資料,讀取完成後開始處理然後再返回,在這期間是不會接收其它請求的,也就是說fpm的子程序同時只能響應乙個請求,只有把這個請求處理完成後才會accept下乙個請求,這一點與nginx的事件驅動有很大的區別,nginx的子程序通過epoll管理套接字,如果乙個請求資料還未傳送完成則會處理下乙個請求,即乙個程序會同時連線多個請求,它是非阻塞的模型,只處理活躍的套接字。

程序之間的通訊有哪些呢? 1 管道,2命名管道,3佇列,4訊號量 5共享記憶體

fpm的master程序與worker程序之間不會直接進行通訊,master通過共享記憶體獲取worker程序的資訊,比如worker程序當前狀態、已處理請求數等,當master程序要殺掉乙個worker程序時則通過傳送訊號的方式通知worker程序。

fpm可以同時監聽多個埠,每個埠對應乙個worker pool,而每個pool下對應多個worker程序,類似nginx中server概念,這些歸屬不同的pool的worker程序任由乙個master管理。(是不是說fpm只能有乙個master)

等不及了,上原始碼吧

worker pool的結構為 fpm_worker_pool_s,pool之間構成乙個鍊錶

//**檔案 php/sapi/fpm/fpm/fpm_worker_pool.h

struct fpm_worker_pool_s ;

這個是當php-fpm.conf解析後,會解析到fpm_worker_pool_config_s。

接下來看下php-fpm的啟動過程

//sapi/fpm/fpm/fpm_main.c

int main(int argc, char *argv)

...//初始化

if(0 > fpm_init(...))

...fpm_is_running = 1;

fcgi_fd = fpm_run(&max_requests);//後面都是worker程序的操作,master程序不會走到下面

parent = 0;

.../* library is already initialized, now init our request */

request = fpm_init_request(fcgi_fd);

zend_first_try

/* end of fastcgi loop */

}fcgi_destroy_request(request);

fcgi_shutdown();}}

裡面涉及到了 兩個主要的過程 php_init 和 php_run

php_init 這裡幹了什麼 主要是進行了一些初始化操作

//sapi/fpm/fpm/fpm.c

int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon, int force_stderr) /*

fpm_globals.prefix = prefix;

fpm_globals.pid = pid;

fpm_globals.run_as_root = run_as_root;

fpm_globals.force_stderr = force_stderr;

if(0 > fpm_php_init_main() ||

0 > fpm_stdio_init_main() ||

0 > fpm_conf_init_main(test_conf, force_daemon) //解析php-fpm配置檔案 ||

0 > fpm_unix_init_main() 啟動程序建立乙個管道pipe 用於和子程序(master程序)通訊,子程序完成初始化後,會通過這個管道給啟動程序發訊息, ||

0 > fpm_scoreboard_init_main() 分配用於記錄worker程序執行資訊的結構,次結構分配在共享記憶體上,

||0 > fpm_pctl_init_main() ||

0 > fpm_env_init_main() ||

0 > fpm_signals_init_main() //建立管道,註冊訊號量sigterm、sigint、sigusr1、sigusr2、sigchld、sigquit這些訊號時將呼叫sig_handler()處理||

0 > fpm_children_init_main() ||

0 > fpm_sockets_init_main() //建立每個worker

pool的socket套接字,啟動後講監聽次socket接受請求||

0 > fpm_worker_pool_init_main() //master的事件管理,用於管理io,定時事件||

0 > fpm_event_init_main()) else}if

(0 > fpm_conf_write_pid())

fpm_stdio_init_final();

zlog(zlog_notice, "fpm is running, pid %d", (int) fpm_globals.parent_pid);

return 0;

}

(1)fpm_conf_init_main():

解析php-fpm.conf配置檔案,分配worker pool記憶體結構並儲存到全域性變數中:fpm_worker_pool_config_s *config,各worker pool配置解析到fpm_worker_pool_s->config中。看下fpm_worker_pool_config_s結構體裡面的屬性是不是很熟悉和

php-fpm.conf一樣

ps關於fpm_worker_pool_config_s裡面只是程序池的屬性,全域性的屬性在fpm_global_config_s 結構體中

//sapi/fpm/fpm/fpm_conf.c

/* * please keep the same order as in fpm_conf.c and in php-fpm.conf.in

*/struct fpm_worker_pool_config_s ;

PHP底層未完待續

從巨集觀上來看,php核心的實現與世界上絕大多數的程式一樣,接收輸入資料,做相應處理然後輸出 返回 結果。我們編寫的 就是php接收的輸入資料,php核心對我們編寫的 進行解釋和運算,最後返回相應的運算結果。然而,php與我們自己平時寫的一般的c程式有所不同的是,我們的程式一般用來解決某個具體問題,...

php編碼規範 未完待續

php編碼規範 未完待續 標準化的重要性和好處 程式設計師可以了解任何 弄清程式的狀況 新人可以很快的適應環境 防止新接觸php的人出於節省時間的需要,自創一套風格並養成終生的習慣 防止新接觸php的人一次次的犯同樣的錯誤 在一致的環境下,人們可以減少犯錯的機會 一 標記 1 2 需要開記php.i...

PHP 設計模式 (未完待續)

一 oop介紹 真理形成的兩大要素 事實和抽象 不要把我們的寶貴的遺產視同兒戲,請珍視這個有序而自由的偉大國度,因為,如果我們蹣跚跌倒,如今的自由和文明將走向毀滅.每人都會得到遺傳給他的一切,他繼承豐富的遺產.二 1 抽象類 class onetrick product inte ce produc...