一句话
PHP 生命周期 = MINIT → RINIT → 执行 → RSHUTDOWN → MSHUTDOWN。
CLI/CGI 模式每个请求跑完整 5 步;FPM 模式只跑中间 3 步(M 阶段进程启动时跑一次);Swoole/Workerman 更进一步,只跑 1 步。
五大阶段
| 阶段 | 触发时机 | 典型工作 |
|---|
| MINIT (Module Init) | 进程启动 | 加载扩展、注册类/函数/常量 |
| RINIT (Request Init) | 每个请求开始 | 初始化 $_GET/$_POST/$_SESSION、扩展请求级状态 |
| Execute | RINIT 之后 | 把 PHP 源码编译成 opcode 并执行 |
| RSHUTDOWN | 请求结束 | 调注册的 shutdown 函数、清理临时变量 |
| MSHUTDOWN | 进程退出 | 卸载扩展、释放永久内存 |
CLI vs FPM vs Swoole
CLI(每次都完整 5 步)
- 启动:MINIT → RINIT → Execute → RSHUTDOWN → MSHUTDOWN → 退出
- 慢 —— 每跑一次都要重新加载扩展、解析 INI、编译 opcode
FPM(M 步只跑一次)
1 2 3 4 5
| [启动] MINIT [请求1] RINIT → Execute → RSHUTDOWN [请求2] RINIT → Execute → RSHUTDOWN ... [退出] MSHUTDOWN
|
- worker 进程常驻,处理 N 个请求才退出(
pm.max_requests) - 配合 OPcache,opcode 缓存,编译只发生一次
Swoole / Workerman(M+R 都只跑一次)
1 2 3 4
| [启动] MINIT → RINIT → 业务初始化($app = new Application()) [请求1] -> handle($req) [请求2] -> handle($req) ...
|
- 全程在内存里,没有 RINIT/RSHUTDOWN 开销
- 框架(Hyperf / EasySwoole)启动时就把容器、路由、ORM 全部初始化好
- 代价:得自己处理状态污染、内存泄漏、协程上下文
一个请求里发生的事(FPM)
- Nginx 接到请求,通过 fastcgi 协议把请求转给 PHP-FPM master
- FPM master 派给空闲 worker(或新建)
- worker 跑 RINIT:填
$_SERVER $_GET $_POST,扩展级 hook - worker 编译 PHP 文件 → opcode(OPcache 命中则跳过)
- 执行 opcode,业务代码跑起来
- 输出 buffer → fastcgi 回 Nginx
- RSHUTDOWN:执行注册的
register_shutdown_function、释放变量 - worker 回到空闲,等下一个请求;处理够
max_requests 后退出
性能影响最大的几点
| 优化点 | 提升倍数 | 说明 |
|---|
| 开 OPcache | 2-5x | 没有它每个请求都重新编译,直接砍 60% 性能 |
opcache.validate_timestamps=0(生产) | +10-20% | 不再 stat 文件检查改动 |
| 用 FPM + 长连接 PDO | +30% | 比 CLI 模式快得多 |
| 切到 Swoole / RoadRunner | 5-10x | 跳过 R 阶段,常驻内存 |
| 预加载(PHP 7.4+ preload) | +5-10% | MINIT 时就编译好核心类 |
SAPI 是什么
SAPI (Server API) = PHP 与外部环境对接的抽象层。CLI、FPM、Apache mod_php、Swoole 都各有自己的 SAPI。
1 2
| php -r 'echo php_sapi_name();'
|
写扩展或框架时,常需要 if (php_sapi_name() === 'cli') 判断当前模式。
调试小技巧
1 2 3 4
| register_shutdown_function(function () { echo "请求结束,内存峰值:" . memory_get_peak_usage(true) . "\n"; });
|
参考