核心内核:单进程,就是程序在操作系统里只创建一个进程实例,整个程序所有逻辑、内存、文件描述符,全部归属这一个进程。类比:一间独立小作坊,只有一个车间,一套工作台,所有活都在这个车间里完成;没有新开别的车间。PHP-CLI简单脚本、Nginx单进程模式、早期简单服务都是单进程。单进程不等于单线程,一个进程内部可以开多个线程。**

一、本源基础术语

  1. 进程 Process
    操作系统资源分配的最小单位,拥有独立虚拟内存空间、独立文件描述符表、pid。
  2. 单进程 Single Process
    程序运行期间,操作系统中仅有一个pid代表本程序,不会fork生成多个子进程。所有业务逻辑跑在这一个进程内部。
  3. pid 进程ID
    内核分配给进程的唯一编号,单进程程序只有唯一pid。
  4. 进程虚拟地址空间
    单进程独享一整套虚拟内存:代码段、数据段、堆、栈、共享库。进程之间内存隔离,别的进程不能直接读写它的内存。
  5. 单进程多线程
    一个进程内部创建多个线程,线程共享进程的堆、文件描述符;每个线程拥有独立栈。Swoole单进程多线程模型就是这个。
  6. 单进程单线程
    进程内只有一条执行流,同一时刻只能执行一段代码。简单PHP CLI脚本默认是单进程单线程。

二、单进程内部内存分区术语

  1. 代码段 Text段
    存放编译后的程序指令,只读,进程内所有线程共享。
  2. 数据段 Data段
    全局变量、静态变量。程序启动就分配。
  3. 堆 heap
    动态申请内存(PHP new、malloc),向上增长,进程内所有线程共享。堆内存由程序自己管理。
  4. 栈 stack
    函数调用、局部变量,每个线程独立栈,函数退出自动回收。
  5. 共享库 / so
    动态链接库,加载到进程虚拟地址空间,多个进程可以复用同一份库代码。

三、单进程的资源术语

  1. 文件描述符表 fd table
    进程私有,这个进程打开的socket、文件、管道全部记录在这张表里。单进程所有IO资源都在这一张fd表,ulimit限制就是限制这个进程最大fd数量。
  2. 打开文件资源
    所有数据库连接、日志文件、网络socket,全部归属这一个进程。进程退出,内核自动关闭这个进程全部fd,释放资源。
  3. 信号掩码 signal mask
    单进程拥有独立信号处理配置。操作系统发送信号(SIGINT、SIGTERM),由这个进程注册的回调处理。
  4. 进程资源限制 ulimit
    限制这个单进程最大打开fd、最大内存、最大CPU时间。

四、执行模型术语

  1. 串行执行
    单进程单线程:代码顺序执行,同一时间只能处理一件任务。一个IO阻塞,整个程序卡住。PHP普通CLI脚本。
  2. 事件驱动(单进程Reactor)
    单进程内部用epoll监听fd,非阻塞IO。不用多进程多线程,在一个进程内同时处理大量连接。Redis就是经典单进程单线程Reactor。

注意:Redis主线程单进程单线程,但后台还有独立子进程做RDB/AOF持久化。

  1. 任务切换(线程切换)
    单进程多线程场景,CPU在进程内不同线程之间切换,属于线程上下文切换,开销远小于进程上下文切换。
  2. 事件循环 Event Loop
    单进程事件驱动程序的核心循环:等待IO事件,事件就绪,执行回调,循环往复。

五、单进程的优缺点术语

  1. 资源开销低
    不需要创建多个进程,没有多进程之间进程上下文切换的巨大开销;内存只有一份进程虚拟空间。
  2. 内存共享简单
    同一个进程内多个线程天然共享堆内存,不用进程间通信IPC,读写共享内存直接访问变量。代价:需要加锁,防止竞态。
  3. 单点故障
    致命短板:只有一个进程。进程一旦崩溃,整个服务直接全部停止。没有别的进程兜底。Redis主线程crash服务就不可用。
  4. 无法利用多核CPU
    单进程单线程:同一时刻只能跑在一个CPU核心,就算机器有8核,其余核心闲置。CPU密集型场景性能上不去。

单进程多线程可以利用多核,线程会被调度到不同核心。

  1. 阻塞传染
    单进程单线程模型,如果业务代码发生阻塞(sleep、同步磁盘IO),整个事件循环卡住,所有连接全部暂停处理。

六、进程间通信IPC(单进程一般不用)

  1. IPC进程间通信
    用于多个进程交换数据。管道、消息队列、共享内存、socket。
    单进程内部线程通信直接读写共享变量,不需要IPC。
  2. fork
    创建子进程,一旦fork,就不再是单进程模型。fork之后变成父子两个独立进程。

七、和多进程模型对比术语

  1. 多进程模型
    启动多个独立进程,每个进程有独立pid、独立虚拟内存。PHP-FPM就是多进程。进程之间内存隔离。
  2. 进程上下文切换
    操作系统从进程A切到进程B,需要切换整个虚拟内存页表,开销很高。单进程模型没有进程上下文切换。
  3. 写时复制 COW
    fork子进程时的机制,父子进程一开始共享内存,一方修改内存才复制页面。单进程不存在COW。

八、工程场景术语

  1. Redis单进程主线程
    经典面试例子:Redis网络事件、命令解析都在单进程单线程,持久化交给fork出来的子进程。
  2. PHP单进程Swoole
    Swoole可以配置单进程多线程模式,一个进程内开启多个worker线程。
  3. 守护进程daemon
    单进程也可以变成daemon,脱离终端后台运行。
  4. 内存泄漏
    单进程长时间运行,持续申请内存不释放,内存只会持续上涨。没有其他进程分担,只能重启进程释放全部内存。

九、认知误区术语

  1. 误区:单进程=单线程
    纠正:单进程只是只有一个进程,内部可以多个线程。进程和线程是两层概念。
  2. 误区:单进程就不能用多核CPU
    纠正:单进程多线程可以调度到多个CPU核心;单进程单线程只能占用单个核心。
  3. 误区:单进程一定性能差
    纠正:IO密集型场景,事件驱动单进程单线程性能极高(Redis),瓶颈不在CPU,在网络IO。CPU密集型场景才受限于单核。
  4. 误区:单进程内部线程崩溃不影响整个进程
    纠正:一个线程段错误crash,整个进程直接退出,所有线程一起挂掉。
  5. 误区:单进程不能监听多个socket
    纠正:epoll可以在一个进程里监听成千上万个fd,同时管理大量TCP连接,Redis就是这样。

类比串联

单进程 = 一间独立作坊

  • 进程:作坊本身,拥有独立厂房(虚拟内存);
  • 单进程单线程:作坊里面只有1个工人,一次只能干一件活;遇到等待(IO),工人原地不动,所有任务停滞;
  • 单进程多线程:作坊里多个工人,共用同一套仓库(堆内存),每个人有自己的工作台(线程栈);
  • 事件循环:工人不停看消息看板epoll,哪个订单就绪处理哪个;
  • 单点故障:作坊一旦失火,所有工作全部停止;
  • 多进程:开很多间独立作坊,每一间互相隔离,一间着火其他还能继续干活。

庖丁解牛一句话总结

单进程指操作系统只分配一个pid、一套独立虚拟内存空间的程序实例;单进程可以是单线程,也可以是多线程;单进程单线程无法利用多核CPU;事件驱动单进程模型擅长IO密集场景,无进程上下文切换开销;缺点是单点故障,进程崩溃服务整体不可用。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐