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

相关推荐
傻啦嘿哟6 小时前
某招聘平台爬虫:爬取招聘岗位数据,分析各城市薪资水平
开发语言·爬虫·python
2501_933670796 小时前
2026秋招量化分析岗技能栈:Python、SQL、统计建模、回测项目怎么准备
开发语言·python·sql
李少兄7 小时前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
Seoyoneh8 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
Tangyuewei8 小时前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界9 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python
繁星蓝雨9 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J10 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
杜大哥10 小时前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python
wuyk55510 小时前
Python网络爬虫入门到实战 第01章:爬虫到底是什么?原理、流程、合法性、风险全解析(零基础必看)
开发语言·爬虫·python