PHP 的进程 fork 机制

PHP 的进程 fork 机制,是整个 PHP Web 运行模式的基石。

核心:Apache/Nginx 如何产生 PHP 进程

PHP 本身并不负责 fork ,fork 动作是由**与 Web Server 配合的 SAPI(Server API)**完成的。最常见的是 PHP-FPM(FastCGI Process Manager)。

1. PHP-FPM 的进程池模型

PHP-FPM 在启动时,会预先 fork 出一堆空闲的 PHP 解释器进程,等着处理请求。

复制代码
启动 PHP-FPM:
  
  Master 进程 (root)
  │
  │ fork() 系统调用
  ├── Worker 进程 1 (空闲)
  ├── Worker 进程 2 (空闲)
  ├── Worker 进程 3 (空闲)
  ├── ... (默认启动几十到几百个)
  └── Worker 进程 N (空闲)
  • Master 进程:负责管理,不处理请求。挂了就 fork 新的补上。
  • Worker 进程:真正干活的。每个 Worker 就是一个完整的、独立的 PHP 解释器实例。
2. 请求到来时的流程
复制代码
1. 用户请求 http://xxx/seckill.php
        │
        ▼
2. Nginx (Web Server)
        │ 通过 FastCGI 协议转发
        ▼
3. PHP-FPM Master 进程
        │ 从空闲进程池挑一个 Worker
        ▼
4. PHP-FPM Worker 进程 7 (PID 12345)
        │ - 这个进程已经被 fork 好了,空转着等活儿
        │ - 它有自己的独立内存空间(包括自己的堆)
        │ - 加载 seckill.php 并执行
        │ - 返回响应给 Nginx
        │ - 进程状态变回"空闲",不会销毁
        ▼
5. Worker 进程 7 继续等待下一个请求

关键点:Worker 进程是复用的(不是每个请求来都要 fork 一次新进程)。

底层:fork() 系统调用的逻辑

Linux 的 fork() 创建一个子进程,核心动作是将父进程的内存完整复制一份

复制代码
父进程 (PID 1000)
┌────────────────────────────┐
│ 代码段                      │
│ 数据段 (stock = 100)        │
│ 堆                           │
│ 栈                           │
│ 文件描述符表                 │
└────────────────────────────┘
         │
         │ fork()
         ▼
子进程 (PID 1001)
┌────────────────────────────┐
│ 代码段 (复制)                │
│ 数据段 (stock = 100 复制)   │  ← 独立!和父进程完全隔离
│ 堆 (复制)                   │
│ 栈 (复制)                   │
│ 文件描述符表 (复制)         │
└────────────────────────────┘

写时复制(Copy-On-Write, COW)优化:

现代 OS 不会真的立刻完全复制内存,太慢了。而是:

  1. fork 刚完成时,父子进程共享同一块物理内存(只读)。
  2. 当任一进程尝试写入某块内存时,OS 才真正复制那块内存给写的那一方。

所以 PHP Worker 进程刚 fork 出来时,和父进程共享内存;一旦 Worker 开始修改 对象的 变量,OS 就为它复制一块新的,从此两者完全隔离。

完整流程一览 比如:

php 复制代码
// seckill.php
$stock = 100;  // 这个变量只在当前 Worker 进程的堆里
复制代码
Nginx 收到两个并发请求
        │
        ├── 请求1 → PHP-FPM → Worker A (PID 100)
        │                        │
        │                        ├── $stock = 100(Worker A 自己的堆)
        │                        ├── $stock-- → 变成 99
        │                        └── 返回 "成功"
        │
        └── 请求2 → PHP-FPM → Worker B (PID 101)
                                 │
                                 ├── $stock = 100(Worker B 自己的堆)← 还是 100!
                                 ├── $stock-- → 变成 99
                                 └── 返回 "成功"
        → 超卖了!因为两个 Worker 各减各的,互不影响。

这就是为什么 PHP 做秒杀只能用 Redis 之类的"外部公务员"来管理库存。

总结

步骤 谁做的 做什么
启动 PHP-FPM Master fork 出一堆 Worker 进程
处理请求 Nginx FastCGI 协议发给某个空闲 Worker
内存隔离 Linux 内核 fork() + COW 保证每个 Worker 内存独立
干掉进程 PHP-FPM Master Worker 处理过多请求后被干掉,fork 新的顶上

所以 PHP 程序员在写代码的时候,完全不需要考虑"另一个用户会不会改了我这个变量",因为每个用户的请求跑在不同的进程、不同的物理内存里

这种"共享无"架构让 PHP 开发心智负担极低,但也正是这个特性,让纯 PHP 进程内缓存不可能存在,必须依赖外部服务来共享数据。

相关推荐
2301_8035389510 分钟前
Java读取Word图片的两种实用方法
java·开发语言·word
AugustRed1 小时前
Linux 运维常用命令大全(超全速查表)
运维·网络·php
bug和崩溃我都要2 小时前
Qt 封装 libmpv 全功能视频播放器开发指南
开发语言·qt·音视频
郝学胜-神的一滴2 小时前
Qt 高级开发 018:复刻经典登录界面布局与窗口美化全解析
开发语言·c++·qt·程序人生·用户界面
郝亚军2 小时前
IEEE 754 单精度浮点的SEM表示
开发语言·c++·算法
zhangjw342 小时前
第15篇:Java多线程零基础入门,进程线程、线程创建方式、线程生命周期、线程安全彻底吃透
java·开发语言·面试
蝈理塘(/_\)大怨种2 小时前
类和对象 (上)
java·开发语言
小新1103 小时前
qt creator 将qInfo的输出日志写入日志文档,方便查看
开发语言·qt
hssfscv3 小时前
QT的学习记录1
开发语言·qt·学习
SunnyDays10113 小时前
Python操作Excel批注:从基础添加到高级自定义的完整指南
开发语言·python·excel