### 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`结构,各结构的对应关系如下图。
![](https://box.kancloud.cn/2149debf6bd2561595c28d3883cc4296_529x484.png)
__(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超时控制的思考