自定义进程与用户进程的区别

解读

在国内 PHP 面试里,这道题并不是考操作系统教科书里的“用户态/内核态”概念,而是借“进程”一词考察候选人是否真正用 PHP 写过常驻内存、异步、并发的后端程序。
“自定义进程”特指在 PHP-FPM 或 Apache 之外,由开发者自己拉起、自己管理的 Worker 进程,常见于 Swoole、WorkerMan、ReactPHP 等常驻内存方案;
“用户进程”则是 Linux 用户空间里的普通进程,PHP-FPM 的 worker、CLI 的一次性脚本、crontab 调起的 php 都属于这一类。
面试官想听的是:你是否清楚两种进程在生命周期、内存模型、通信方式、重启策略、监控手段上的差异,以及线上如何取舍。

知识点

  1. PHP 生命周期模型:RINIT→脚本执行→RSHUTDOWN 与常驻内存的 CLI/Swoole 差异
  2. Linux 进程三态:运行、就绪、阻塞;用户空间与内核空间切换成本
  3. 进程间通信:Unix Domain Socket、消息队列、共享内存、pipe、信号
  4. 进程管理:supervisor、systemd、Swoole\Process::wait()、WorkerMan 的 master-manager-worker 三级模型
  5. 内存隔离:COW(写时复制)、OPcache 共享、常驻内存里静态变量/单例的污染问题
  6. 热重启:平滑 reload(USR2)、onWorkerStop、opcache_reset() 时机
  7. 监控指标:RSS、PSS、fd 数、qps、worker 空闲率、重启次数
  8. 国内云原生场景:Kubernetes 的 sidecar 日志采集、Swoole 镜像最小化、alpine 的 musl libc 与 gdb 调试陷阱

答案

一句话先给结论:
“自定义进程”是我们在 PHP 里为了常驻内存、异步任务、高并发而手动编写的 Worker 进程,生命周期由代码控制;
“用户进程”是 Linux 视角下所有运行在用户空间的普通进程,PHP-FPM、CLI、crontab 调起的脚本都算,生命周期由内核和父进程决定。
具体差异体现在以下四点:

  1. 生命周期与重启策略
    自定义进程通常由 master 进程 fork,异常退出后 master 会立即重新拉起,实现“自愈”;用户进程若未托管给 supervisor 则一旦退出就真正消失。
  2. 内存与变量模型
    自定义进程常驻内存,静态变量、单例、类映射在多次请求间不会重置,需要开发者手动 unset 或周期性 reload;用户进程(FPM)每次请求结束即释放,内存隔离干净,但无法做连接池、缓存复用。
  3. 通信方式
    自定义进程常用 UnixSocket 或 Channel 与 master 通信,可零拷贝投递任务;用户进程之间默认彼此隔离,只能通过外部队列(Redis、Kafka)或文件 DB 通信。
  4. 监控与运维
    自定义进程需要自己在代码里暴露 metrics 接口,配合 Prometheus+Grafana;用户进程可直接复用 php-fpm_exporter、node_exporter 等社区方案,云原生接入成本更低。
    线上取舍:
    短连接、无状态业务继续用 FPM;需要维持 TCP 长连接、毫秒级推送、异步任务的消费端,就写自定义进程,并用 supervisor 或 systemd 做二级守护,保证高可用。

拓展思考

  1. 如果自定义进程里用了 Laravel ORM,怎样避免 Model 静态属性跨请求污染?
    答:在 onWorkerStart 回调里重新 new Application 并刷新 Container,或者使用 LaravelS/Octane 提供的“sandbox”机制,每次请求克隆一份 Container。
  2. 国内云厂商的函数计算(SCF、FC)号称“零运维”,是否还需要理解自定义进程?
    答:需要。函数计算底层仍是容器+常驻进程,只是平台帮你托管了 master;理解自定义进程才能调优冷启动、内存驻留、连接池复用,否则依旧会出现“第一次请求慢、RDS 连接打爆”的经典坑。