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 进程做的第一件事

  1. 检查运行用户(user / group)

  2. 检查是否已有 pid 文件(防止重复启动)

  3. 设置运行模式

    • daemonize(后台)
    • foreground(容器里常用)

3. 配置加载与解析阶段

3.1 加载主配置文件

顺序是固定的:

  1. php-fpm.conf
  2. include=php-fpm.d/*.conf
  3. 每个 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:9000
    • listen = /run/php-fpm.sock
  • 进程管理方式

    • pm = static | dynamic | ondemand
  • 运行用户

    • user / group
  • PHP 环境

    • php_admin_value
    • php_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:

  1. fork 自 Master
  2. 切换用户(setuid)
  3. 初始化 PHP Runtime
  4. 加载 php.ini
  5. 加载扩展(redis、pdo、grpc…)
  6. 初始化 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 -t
  • journalctl -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 只是“执行引擎”,不是“路由器”