### 1.3.3 FPM的初始化
接下來看下fpm的啟動流程,從`main()`函數開始:
```c
//sapi/fpm/fpm/fpm_main.c
int 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():__
```c
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()`處理:
```c
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進程的。
```c
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進程的后續操作。
- 前言
- 第1章 PHP基本架構
- 1.1 PHP簡介
- 1.2 PHP7的改進
- 1.3 FPM
- 1.3.1 概述
- 1.3.2 基本實現
- 1.3.3 FPM的初始化
- 1.3.4 請求處理
- 1.3.5 進程管理
- 1.4 PHP執行的幾個階段
- 第2章 變量
- 2.1 變量的內部實現
- 2.2 數組
- 2.3 靜態變量
- 2.4 全局變量
- 2.5 常量
- 第3章 Zend虛擬機
- 3.1 PHP代碼的編譯
- 3.1.1 詞法解析、語法解析
- 3.1.2 抽象語法樹編譯流程
- 3.2 函數實現
- 3.2.1 內部函數
- 3.2.2 用戶函數的實現
- 3.3 Zend引擎執行流程
- 3.3.1 基本結構
- 3.3.2 執行流程
- 3.3.3 函數的執行流程
- 3.3.4 全局execute_data和opline
- 3.4 面向對象實現
- 3.4.1 類
- 3.4.2 對象
- 3.4.3 繼承
- 3.4.4 動態屬性
- 3.4.5 魔術方法
- 3.4.6 類的自動加載
- 3.5 運行時緩存
- 3.6 Opcache
- 3.6.1 opcode緩存
- 3.6.2 opcode優化
- 3.6.3 JIT
- 第4章 PHP基礎語法實現
- 4.1 類型轉換
- 4.2 選擇結構
- 4.3 循環結構
- 4.4 中斷及跳轉
- 4.5 include/require
- 4.6 異常處理
- 第5章 內存管理
- 5.1 Zend內存池
- 5.2 垃圾回收
- 第6章 線程安全
- 6.1 什么是線程安全
- 6.2 線程安全資源管理器
- 第7章 擴展開發
- 7.1 概述
- 7.2 擴展的實現原理
- 7.3 擴展的構成及編譯
- 7.3.1 擴展的構成
- 7.3.2 編譯工具
- 7.3.3 編寫擴展的基本步驟
- 7.3.4 config.m4
- 7.4 鉤子函數
- 7.5 運行時配置
- 7.5.1 全局變量
- 7.5.2 ini配置
- 7.6 函數
- 7.6.1 內部函數注冊
- 7.6.2 函數參數解析
- 7.6.3 引用傳參
- 7.6.4 函數返回值
- 7.6.5 函數調用
- 7.7 zval的操作
- 7.7.1 新生成各類型zval
- 7.7.2 獲取zval的值及類型
- 7.7.3 類型轉換
- 7.7.4 引用計數
- 7.7.5 字符串操作
- 7.7.6 數組操作
- 7.8 常量
- 7.9 面向對象
- 7.9.1 內部類注冊
- 7.9.2 定義成員屬性
- 7.9.3 定義成員方法
- 7.9.4 定義常量
- 7.9.5 類的實例化
- 7.10 資源類型
- 7.11 經典擴展解析
- 7.8.1 Yaf
- 7.8.2 Redis
- 第8章 命名空間
- 8.1 概述
- 8.2 命名空間的定義
- 8.2.1 定義語法
- 8.2.2 內部實現
- 8.3 命名空間的使用
- 8.3.1 基本用法
- 8.3.2 use導入
- 8.3.3 動態用法
- 附錄
- break/continue按標簽中斷語法實現
- defer推遲函數調用語法的實現
- 一起線上事故引發的對PHP超時控制的思考