知识卡片

PPC与prefork:每连接一进程模型的三大瓶颈

结构图卡

内容

PPC(Process Per Connection)是每来一个新连接就fork一个新进程专门处理这个连接的传统UNIX网络服务器模型:父进程accept连接后fork出子进程,子进程负责这个连接的读写和业务处理,处理完后子进程close连接(父进程fork后立刻调用的close只是把连接文件描述符的引用计数减一,真正关闭要等子进程也close、引用计数归零才发生)。这个模型实现简单,在连接数不多的场景(如数据库服务器)或互联网兴起前的普通业务场景下运作良好,世界上第一个web服务器CERN httpd用的就是这套模型,但随着互联网并发量从几十暴涨到成千上万,它的三个瓶颈就凸显出来了:一是fork代价高——创建一个进程需要操作系统分配大量内核资源、把父进程的内存映像复制给子进程,即使有Copy on Write优化,总体代价依然很大;二是父子进程通信复杂——fork时文件描述符能通过内存映像复制传给子进程,但fork完成后父子进程之间要交换信息(比如子进程要把自己处理了多少请求汇报给父进程做全局统计)就必须借助IPC这类进程间通信机制,实现起来比较麻烦;三是支持的并发连接数量有限——如果连接存活时间长且新连接源源不断,进程数会持续膨胀,操作系统进程调度和切换的开销也随之上升,PPC方案通常最多也就能撑住几百并发连接。prefork是为了解决”fork代价高导致用户首次访问变慢”这个问题而生的变体:系统启动时就提前把进程创建好,等真正有连接进来时就省去了现场fork的开销;实现的关键是让多个子进程都去accept同一个socket,操作系统保证最终只有一个进程能accept成功,但这会带来”惊群”现象——虽然只有一个子进程真正accept成功,所有阻塞在accept上的子进程却都会被唤醒,造成不必要的进程调度和上下文切换(好在Linux 2.6内核之后这个问题已经被操作系统层面解决)。prefork虽然省去了现场fork的延迟,但父子进程通信复杂、并发连接数有限这两个PPC固有的瓶颈依然没有解决,因此实际应用不算多,Apache的MPM prefork模式默认最大只支持256个并发连接,通常只在需要高可靠性或兼容旧软件的场景下才被推荐使用。

结构图

flowchart TB
  A["PPC:每连接一进程"]
  A --> B["瓶颈①fork代价高<br/>需分配内核资源+复制内存映像"]
  A --> C["瓶颈②父子进程通信复杂<br/>需借助IPC机制"]
  A --> D["瓶颈③并发连接数有限<br/>进程调度切换开销随连接数飙升,通常上限几百"]
  A --> E["prefork变体:提前预创建进程<br/>省去现场fork延迟"]
  E --> F["解决了:首次访问慢的问题"]
  E --> G["新增:惊群现象<br/>多子进程accept同一socket,唤醒代价<br/>(Linux 2.6+内核层已解决)"]
  E --> H["未解决:父子进程通信复杂+连接数有限<br/>实际应用不多,Apache MPM prefork默认256并发上限"]

参考来源

- 位置:《从零开始学架构》第18讲《单服务器高性能模式:PPC与TPC》"PPC""prefork"(源文件:_epub-src/OEBPS/text00001.html) - 结论依据:原文说明PPC三大问题"fork 代价高……父子进程通信复杂……支持的并发连接数量有限……PPC 方案能处理的并发连接数量最大也就几百",并说明prefork"存在'惊群'现象……prefork 模式和 PPC 一样,还是存在父子进程通信复杂、支持的并发连接数量有限的问题……Apache 服务器提供了 MPM prefork 模式……默认情况下最大支持 256 个并发连接",直接支撑本卡片结论与结构图。 - 原始内容:fork 代价高:站在操作系统的角度,创建一个进程的代价是很高的……父子进程通信复杂:父进程"fork"子进程时……需要采用 IPC……支持的并发连接数量有限……prefork 就是提前创建进程……存在'惊群'现象……prefork 模式和 PPC 一样,还是存在父子进程通信复杂、支持的并发连接数量有限的问题。