FPM的初始化
接下來看下fpm的啟動流程,從main()函數開始://sapi/fpm/fpm/fpm_main.cint?main(int?argc,?char?*argv[]){
...????//注冊SAPI:將全局變量sapi_module設置為cgi_sapi_module
sapi_startup(&cgi_sapi_module);
...????//執行php_module_starup()
if?(cgi_sapi_module.startup(&cgi_sapi_module)?==?FAILURE)?{????????return?FPM_EXIT_SOFTWARE;
}
...????//初始化
if(0?>?fpm_init(...)){
...
}
...
fpm_is_running?=?1;
fcgi_fd?=?fpm_run(&max_requests);//后面都是worker進程的操作,master進程不會走到下面
parent?=?0;
...
}
fpm_init()主要有以下幾個關鍵操作:
(1)fpm_conf_init_main():
解析php-fpm.conf配置文件,分配worker pool內存結構并保存到全局變量中:fpm_worker_all_pools,各worker pool配置解析到fpm_worker_pool_s->config中。
(2)fpm_scoreboard_init_main():?分配用于記錄worker進程運行信息的共享內存,按照worker pool的最大worker進程數分配,每個worker pool分配一個fpm_scoreboard_s結構,pool下對應的每個worker進程分配一個fpm_scoreboard_proc_s結構,各結構的對應關系如下圖。
(3)fpm_signals_init_main():static?int?sp[2];int?fpm_signals_init_main(){????struct?sigaction?act;
//創建一個全雙工管道
if?(0?>?socketpair(AF_UNIX,?SOCK_STREAM,?0,?sp))?{????????return?-1;
}????//注冊信號處理handler
act.sa_handler?=?sig_handler;
sigfillset(&act.sa_mask);????if?(0?>?sigaction(SIGTERM,??&act,?0)?||????????0?>?sigaction(SIGINT,???&act,?0)?||????????0?>?sigaction(SIGUSR1,??&act,?0)?||????????0?>?sigaction(SIGUSR2,??&act,?0)?||????????0?>?sigaction(SIGCHLD,??&act,?0)?||????????0?>?sigaction(SIGQUIT,??&act,?0))?{????????return?-1;
}????return?0;
}
這里會通過socketpair()創建一個管道,這個管道并不是用于master與worker進程通信的,它只在master進程中使用,具體用途在稍后介紹event事件處理時再作說明。另外設置master的信號處理handler,當master收到SIGTERM、SIGINT、SIGUSR1、SIGUSR2、SIGCHLD、SIGQUIT這些信號時將調用sig_handler()處理:static?void?sig_handler(int?signo){????static?const?char?sig_chars[NSIG?+?1]?=?{
[SIGTERM]?=?'T',
[SIGINT]??=?'I',
[SIGUSR1]?=?'1',
[SIGUSR2]?=?'2',
[SIGQUIT]?=?'Q',
[SIGCHLD]?=?'C'
};????char?s;
...
s?=?sig_chars[signo];????//將信號通知寫入管道sp[1]端
write(sp[1],?&s,?sizeof(s));
...
}
(4)fpm_sockets_init_main()
創建每個worker pool的socket套接字。
(5)fpm_event_init_main():
啟動master的事件管理,fpm實現了一個事件管理器用于管理IO、定時事件,其中IO事件通過kqueue、epoll、poll、select等管理,定時事件就是定時器,一定時間后觸發某個事件。
在fpm_init()初始化完成后接下來就是最關鍵的fpm_run()操作了,此環節將fork子進程,啟動進程管理器,另外master進程將不會再返回,只有各worker進程會返回,也就是說fpm_run()之后的操作均是worker進程的。int?fpm_run(int?*max_requests){????struct?fpm_worker_pool_s?*wp;
for?(wp?=?fpm_worker_all_pools;?wp;?wp?=?wp->next)?{????????//調用fpm_children_make()?fork子進程
is_parent?=?fpm_children_create_initial(wp);
if?(!is_parent)?{????????????goto?run_child;
}
}????//master進程將進入event循環,不再往下走
fpm_event_loop(0);
run_child:?//只有worker進程會到這里
*max_requests?=?fpm_globals.max_requests;????return?fpm_globals.listening_socket;?//返回監聽的套接字}
在fork后worker進程返回了監聽的套接字繼續main()后面的處理,而master將永遠阻塞在fpm_event_loop(),接下來分別介紹master、worker進程的后續操作。