php-fpm启动过程
1. php-fpm 启动的整体脉络
总览:
Master 进程启动 → 解析配置 → 初始化运行环境 → 创建监听 Socket → 按进程管理模式 fork Worker → 等待 Web Server 连接 → 处理请求
2. 启动入口与 Master 进程
2.1 启动命令来源
常见几种方式本质一样:
- systemd
systemctl start php-fpm - 直接命令
php-fpm/php-fpm -D - Docker
CMD ["php-fpm", "-F"]
最终都会进入 php-fpm master 进程。
2.2 Master 进程做的第一件事
检查运行用户(user / group)
检查是否已有 pid 文件(防止重复启动)
设置运行模式
- daemonize(后台)
- foreground(容器里常用)
3. 配置加载与解析阶段
3.1 加载主配置文件
顺序是固定的:
php-fpm.confinclude=php-fpm.d/*.conf- 每个 pool 的配置文件(如
www.conf)
关键点:
- 这里已经决定了 PHP 程序“能否被访问”,但还没执行 PHP
- 配置错误会直接导致启动失败
root@9df56040450b:/usr/local/etc# egrep -v '^;|^$' php-fpm.d/www.conf
[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
3.2 pool 配置解析重点
每个 pool 会解析:
监听方式
listen = 127.0.0.1:9000listen = /run/php-fpm.sock
进程管理方式
pm = static | dynamic | ondemand
运行用户
user / group
PHP 环境
php_admin_valuephp_value
这里决定的是:将来 fork 出来的 Worker 长什么样。
4. 运行环境初始化
4.1 Master 初始化资源
创建 pid 文件
打开 error_log
初始化共享内存(scoreboard)
设置 signal handler
- SIGTERM
- SIGUSR1 / SIGUSR2(reload)
4.2 创建监听 Socket
按 pool 配置:
TCP Socket:
- bind IP:PORT
Unix Socket:
- 创建 sock 文件
- 设置权限 / owner / group
如果这里失败:
- 端口占用
- sock 权限不对
- SELinux / AppArmor
php-fpm 会 直接启动失败。
5. Worker 进程 fork 阶段(核心)
5.1 进程管理模式的影响
static
- 启动时一次性 fork
pm.max_children - 启动慢,但请求稳定
dynamic(默认)
启动 fork:
pm.start_servers
根据压力动态增减
ondemand
- 启动时 不 fork
- 第一个请求到来才创建 Worker
5.2 Worker 初始化流程
每一个 Worker:
- fork 自 Master
- 切换用户(setuid)
- 初始化 PHP Runtime
- 加载 php.ini
- 加载扩展(redis、pdo、grpc…)
- 初始化 opcode cache(Opcache)
如果你感觉 php-fpm 启动慢,80% 卡在这一步
6. 就绪状态:等待请求
6.1 Master 的职责
- 监听 socket
- 监控 Worker 状态
- 回收异常退出的 Worker
- 根据 pm 策略调整进程数
6.2 Worker 的职责
- accept 请求
- 解析 FastCGI 协议
- 执行 PHP 脚本
- 返回结果
- 等待下一个请求
7. 完整的启动流程图
flowchart TD
A[启动 php-fpm] --> B[Master 进程创建]
B --> C[加载 php-fpm.conf]
C --> D[加载 pool 配置]
D --> E[初始化运行环境]
E --> F[创建监听 Socket]
F --> G{pm 模式}
G -->|static| H[一次性 fork Worker]
G -->|dynamic| I[按 start_servers fork]
G -->|ondemand| J[等待请求]
H --> K[Worker 初始化 PHP Runtime]
I --> K
J --> K
K --> L[进入就绪态]
L --> M[处理 FastCGI 请求]
8. 从运维角度看,启动阶段你该盯什么
8.1 启动失败
php-fpm -tjournalctl -u php-fpm- sock / 端口 / 权限
root@9df56040450b:/usr/local/etc# php-fpm --help
Usage: php [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix>] [-g <pid>] [-c <file>] [-d foo[=bar]] [-y <file>] [-D] [-F [-O]]
-c <path>|<file> Look for php.ini file in this directory
-n No php.ini file will be used
-d foo[=bar] Define INI entry foo with value 'bar'
-e Generate extended information for debugger/profiler
-h This help
-i PHP information
-m Show compiled in modules
-v Version number
-p, --prefix <dir>
Specify alternative prefix path to FastCGI process manager (default: /usr/local).
-g, --pid <file>
Specify the PID file location.
-y, --fpm-config <file>
Specify alternative path to FastCGI process manager config file.
-t, --test Test FPM configuration and exit # 测试php-fpm关联配置是否正确
-D, --daemonize force to run in background, and ignore daemonize option from config file
-F, --nodaemonize
force to stay in foreground, and ignore daemonize option from config file
-O, --force-stderr
force output to stderr in nodaemonize even if stderr is not a TTY
-R, --allow-to-run-as-root
Allow pool to run as root (disabled by default)
root@9df56040450b:/usr/local/etc# php-fpm -t
[25-Dec-2025 10:08:55] NOTICE: configuration file /usr/local/etc/php-fpm.conf test is successful
8.2 启动慢
- 扩展过多
- Opcache 预热
- pm.static + max_children 太大
- 容器 CPU 限制
8.3 启动成功但无法访问
- nginx fastcgi_pass 指错
- sock 权限不匹配
- listen.allowed_clients
9. 一个现实的认知校正
php-fpm 不关心你的 PHP 程序路径。
- 它只执行 Web Server 通过 FastCGI 传给它的:
- SCRIPT_FILENAME (php-fpm是作为php的执行解释器的管理进程的)
- 代码目录是 Nginx / Apache 决定的
- php-fpm 只是“执行引擎”,不是“路由器”