?
本文檔使用 php中文網(wǎng)手冊 發(fā)布
FPM的初始化
接下來看下fpm的啟動流程,從main()函數(shù)開始:
//sapi/fpm/fpm/fpm_main.cint main(int argc, char *argv[]){ ... //注冊SAPI:將全局變量sapi_module設(shè)置為cgi_sapi_module sapi_startup(&cgi_sapi_module); ... //執(zhí)行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進(jìn)程的操作,master進(jìn)程不會走到下面 parent = 0; ... }
fpm_init()主要有以下幾個關(guān)鍵操作:
(1)fpm_conf_init_main():
解析php-fpm.conf配置文件,分配worker pool內(nèi)存結(jié)構(gòu)并保存到全局變量中:fpm_worker_all_pools,各worker pool配置解析到fpm_worker_pool_s->config中。
(2)fpm_scoreboard_init_main(): 分配用于記錄worker進(jìn)程運行信息的共享內(nèi)存,按照worker pool的最大worker進(jìn)程數(shù)分配,每個worker pool分配一個fpm_scoreboard_s結(jié)構(gòu),pool下對應(yīng)的每個worker進(jìn)程分配一個fpm_scoreboard_proc_s結(jié)構(gòu),各結(jié)構(gòu)的對應(yīng)關(guān)系如下圖。
(3)fpm_signals_init_main():
static int sp[2];int fpm_signals_init_main(){ struct sigaction act; //創(chuàng)建一個全雙工管道 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()創(chuàng)建一個管道,這個管道并不是用于master與worker進(jìn)程通信的,它只在master進(jìn)程中使用,具體用途在稍后介紹event事件處理時再作說明。另外設(shè)置master的信號處理handler,當(dāng)master收到SIGTERM、SIGINT、SIGUSR1、SIGUSR2、SIGCHLD、SIGQUIT這些信號時將調(diào)用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()
創(chuàng)建每個worker pool的socket套接字。
(5)fpm_event_init_main():
啟動master的事件管理,fpm實現(xiàn)了一個事件管理器用于管理IO、定時事件,其中IO事件通過kqueue、epoll、poll、select等管理,定時事件就是定時器,一定時間后觸發(fā)某個事件。
在fpm_init()初始化完成后接下來就是最關(guān)鍵的fpm_run()操作了,此環(huán)節(jié)將fork子進(jìn)程,啟動進(jìn)程管理器,另外master進(jìn)程將不會再返回,只有各worker進(jìn)程會返回,也就是說fpm_run()之后的操作均是worker進(jìn)程的。
int fpm_run(int *max_requests){ struct fpm_worker_pool_s *wp; for (wp = fpm_worker_all_pools; wp; wp = wp->next) { //調(diào)用fpm_children_make() fork子進(jìn)程 is_parent = fpm_children_create_initial(wp); if (!is_parent) { goto run_child; } } //master進(jìn)程將進(jìn)入event循環(huán),不再往下走 fpm_event_loop(0); run_child: //只有worker進(jìn)程會到這里 *max_requests = fpm_globals.max_requests; return fpm_globals.listening_socket; //返回監(jiān)聽的套接字}
在fork后worker進(jìn)程返回了監(jiān)聽的套接字繼續(xù)main()后面的處理,而master將永遠(yuǎn)阻塞在fpm_event_loop(),接下來分別介紹master、worker進(jìn)程的后續(xù)操作。