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 进程内缓存不可能存在,必须依赖外部服务来共享数据。

相关推荐
yujunl1 小时前
U9 WCF调试的一个坑
开发语言
lly2024061 小时前
Scala 模式匹配
开发语言
2zcode1 小时前
基于MATLAB卷积神经网络的多颜色车牌识别系统设计与实现
开发语言·matlab·cnn
无限进步_1 小时前
【C++】从红黑树到 map 和 set:封装设计与迭代器实现
开发语言·数据结构·数据库·c++·windows·github·visual studio
Hello eveybody1 小时前
介绍一下动态树LCT(Python)
开发语言·python·算法
handler011 小时前
速通蓝桥杯省一:二分算法
c语言·开发语言·c++·笔记·算法·职场和发展·蓝桥杯
lbb 小魔仙1 小时前
DolphinDB:以“存算一体“重新定义工业时序数据的边界
开发语言·人工智能·python·langchain·jenkins
callJJ1 小时前
Codex 联动 OpenSpec 提效方法论
java·开发语言·codex·openspec
上弦月-编程1 小时前
Java编程:跨平台开发利器
java·开发语言