Skip to content

进程、线程与 IPC

基于 Chromium 现代架构 · 核于 2026-07

速查

  • CPU:计算机的「大脑」,擅长顺序处理五花八门的任务;现代 CPU 多核,每核可独立干活
  • GPU:擅长同时在海量小核上跑简单任务,为图形而生;页面合成、动画上屏靠它
  • 进程(process)= 应用「正在执行的程序」:OS 启动它时划给一块私有内存;进程结束,OS 整块回收
  • 线程(thread)= 住在进程里的执行单元,可执行进程程序的任意部分;同进程内线程共享内存
  • 崩溃半径不同:一个线程崩 → 整个进程崩一个进程崩 → 其他进程无恙——多进程架构的立足点
  • 进程之间内存互不可见,协作必须走 IPC(Inter-Process Communication,进程间通信)
  • 进程可以请求 OS 再启动新进程分担工作(如 browser 进程拉起 renderer);新进程有自己的内存,多份进程会重复占内存
  • Chrome 里的关键 IPC:browser → renderer 的 commit navigation、renderer → browser 的 onload 完成通知、compositor 帧提交给 Viz
  • 对前端:JS 跑在某个 renderer 的主线程postMessage(页面 ↔ Worker、页面 ↔ 跨站 iframe)本质就是「不共享内存、传消息」的同款思路

一、先分清两块硬件:CPU 与 GPU

理解浏览器架构,要先从它脚下的硬件与操作系统说起。

**CPU(Central Processing Unit,中央处理器)**是计算机的大脑。一个 CPU 核心像一位全能工人:什么任务都能接,但一次干一件,按顺序来。现代 CPU 通常有多个核心,意味着可以同时有几位「全能工人」并行干活。

**GPU(Graphics Processing Unit,图形处理器)**走另一条路:单个核心很「笨」、只擅长简单任务,但胜在数量极多、同时开工。它最初为图形处理而生——把百万像素各自算色值,正是「简单任务 × 海量并行」的典型场景;如今 GPU 也被用于图形之外的通用计算。

对前端的意义:浏览器把「算」和「画」拆给两者——解析、布局、跑 JS 主要在 CPU;把已经画好的图层合成上屏则交给 GPU。这是后面「compositor 线程 / Viz 进程」的硬件背景,也是 transform/opacity 动画便宜的底层原因(细节见浏览器渲染原理)。

顺手记两个 JS 里就能摸到的硬件接口:

js
// 逻辑核心数:给 Worker 池定容量的常用依据(注意是逻辑核,含超线程)
console.log(navigator.hardwareConcurrency); // 例如 8

// 设备内存档位(GB,取值被刻意粗化为 0.25/0.5/1/2/4/8):
// Chrome 决定进程数上限时看的也是这类硬件水位
console.log(navigator.deviceMemory); // 例如 8

二、进程与线程:一块私有内存和里面的执行者

应用程序跑起来时,OS 视角看到的是进程。启动一个应用 = OS 创建一个进程,并划出一块私有的内存空间(memory slab)供它存放一切状态。线程则住在进程内部,负责实际执行程序的任意片段;一个进程可以只有一条线程,也可以有很多条。

text
┌─ 进程 A ──────────────┐      ┌─ 进程 B ──────────────┐
│  私有内存(互不可见)    │      │  私有内存              │
│  ├─ 线程 1 ──┐         │ IPC  │  ├─ 线程 1             │
│  ├─ 线程 2 ──┼─ 共享 A  │◄────►│  └─ 线程 2             │
│  └─ 线程 3 ──┘ 的内存   │      │                        │
└───────────────────────┘      └───────────────────────┘

两者最关键的差异是内存归属崩溃半径

维度同进程内的多线程多个进程
内存共享进程的私有内存,随手互读互写彼此不可见,各有一块
通信成本低(直接读写共享状态)高(必须走 IPC 传消息)
一处崩溃整个进程一起死只死自己,其他进程照常
资源回收进程关闭时 OS 整块回收其内存

「线程崩 → 进程崩」值得多咀嚼一句:线程们共享同一块内存,一条线程把内存写坏,同进程的所有线程都不可信了,OS 只能整个进程收掉。反过来,进程之间因为内存隔离,一个进程再怎么崩也污染不到别人——这就是浏览器选择多进程的全部理由的种子:把「容易崩、不可信」的网页代码圈进独立进程,崩溃和恶意行为都被圈在墙内(展开见多进程架构)。

三、程序如何被 OS 执行

把链条串起来:

  1. 启动:你双击浏览器图标,OS 创建 browser 进程、分配私有内存,程序开始在其线程上执行。
  2. 派生:进程可以请求 OS 创建新进程去分担工作——browser 进程为每个 tab 拉起 renderer 进程正是这一机制。新进程拿到自己的内存;如果两边要共用数据,只能复制一份过去或传消息。
  3. 代价:因为内存不共享,公共基础设施会在每个进程里各存一份(Chrome 里最典型的是 V8 引擎的拷贝)——进程越多,重复内存越多。
  4. 回收:进程退出(正常或崩溃),OS 把那块私有内存整体收回。一个 renderer 崩了,它占的内存立刻还给系统,别的进程毫发无损。

3.1 调度:线程多于核心怎么办

真正被 CPU 核心执行的单位是线程。系统里就绪的线程数远多于核心数,OS 调度器按**时间片(time slice)**把线程轮流分派到各核心上,切换时保存/恢复现场(上下文切换,有成本)。两点浏览器相关的推论:

  • 「浏览器几十个进程」不等于「几十个核心在烧」——大多数线程大部分时间在(等网络、等 IPC、等 vsync),调度器只把核心分给有活干的线程;
  • 同一进程内的线程可以被调度到不同核心真正并行(raster 线程 ×N 正是为了吃满多核),而 JS 主线程再忙也只占一个核——想利用多核,只能开 Worker。

更深的调度算法属操作系统课范畴;对理解浏览器架构,抓住「进程 = 私有内存 + 崩溃边界,线程 = 调度与执行单元」即可。

四、IPC:隔离之后,怎么协作

进程彼此看不见对方内存,但浏览器的各部分必须协作——地址栏在 browser 进程,页面内容在 renderer 进程,画面上屏在 Viz 进程。解法是 IPC(Inter-Process Communication,进程间通信):进程之间通过操作系统提供的通道互发消息,而不是共享变量。

Chrome 中几条贯穿本叶的典型 IPC:

消息方向时机
commit navigationbrowser → renderer响应数据就绪,通知 renderer 接管导航并随附数据流
navigation commit 确认renderer → browserrenderer 确认接管,browser 才更新地址栏与会话历史
onload 完成renderer → browser所有 frame 的 onload 跑完,browser 停掉 tab 的 spinner
compositor frame 提交renderer → Viz合成器产出一帧,交给 Viz 聚合上屏
导航请求renderer → browser页面内点链接 / JS 改 window.location 发起新导航

消息传递比共享内存慢,但换来了边界清晰:每条跨进程交互都是显式的、可审计的——沙箱里的 renderer 想做特权操作(发网络请求、写文件),只能「打报告」请 browser/服务进程代办,而不是自己伸手。

五、前端视角:你早就在写「多进程风格」的代码

  • JS 的单线程指的是:你的代码默认只跑在某个 renderer 进程的主线程上。浏览器整体高度并行,但分给你的执行权只有这一条线程——主线程被长任务占住,解析、渲染、交互全排队。
  • Web Worker / Service Worker 是把 JS 挪到同进程(或其他进程)的其他线程;它们与页面不共享内存,靠 postMessage 传消息——与 IPC 同一种世界观。
  • 跨站 iframe 在站点隔离下干脆真的在另一个进程里(见站点隔离),postMessage 此时就是货真价实的跨进程 IPC。
  • 与网络分层的类比:进程隔离 + IPC 之于浏览器,正如「分层 + 协议」之于网络(见网络分层)——都是用明确边界换取可组合与可控。

把 Web 平台的通信原语按「共享 or 传消息」归类,会发现它完整复刻了本页的取舍:

原语模型备注
postMessage + 结构化克隆传消息(数据复制页面 ↔ Worker、页面 ↔ iframe;大对象复制有成本
Transferable(如 ArrayBuffer)传消息(所有权转移零拷贝,转移后原持有方不可再用
SharedArrayBuffer真·共享内存正因像「同进程线程」般危险,Spectre 后要求跨源隔离(COOP+COEP 头)才开放
BroadcastChannel传消息(一对多)同源的多个 tab/Worker 广播

SharedArrayBuffer 的遭遇是本叶主题的绝佳注脚:一旦允许共享内存,就等于把「同进程」的攻击面(高精度计时 → Spectre 侧信道)交给了网页,于是浏览器反过来要求页面先用 COOP/COEP 声明自我隔离——进程/内存边界的每一次放松,都要用更强的隔离承诺来赎买。

小结

CPU 擅长顺序通用计算、GPU 擅长海量并行的简单任务,浏览器把「算」与「画」分派给两者。进程是 OS 分配了私有内存的运行中程序,线程住在进程里共享其内存:线程崩会拖死整个进程,进程之间却互不牵连——这条「崩溃半径」差异是浏览器多进程架构的立足点。隔离带来协作问题,答案是 IPC 消息传递:commit navigation、onload 通知、合成帧提交都是跨进程消息。前端里的 Worker 与 postMessage 正是同一世界观在应用层的投影。下一页看 Chrome 如何用这些积木搭出多进程架构