浏览器的多进程架构与渲染过程

文章笔记: 前端都该懂的浏览器工作原理,你懂了吗?

进程 (process) 与 线程 (thread)

  • 进程: 程序的一次执行过程, 是一个动态概念, 是程序在执行过程中分配和管理资源的基本单位.
  • 线程: CPU 调度和分派的基本单位, 它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

线程是跑在进程里面的, 一个进程里面可能有一个或者多个线程, 而一个线程, 只能隶属于一个进程.
进程相互独立, 通信依靠 IPC 机制 (Inter Process Communication) 来进行.

浏览器的多进程架构

以 Chrome 为例, 有 4 个主要进程:

  • 浏览器进程 (Browser Process)
    负责浏览器的 Tab 的前进, 后退, 地址栏, 书签栏的工作和处理浏览器的一些不可见的底层操作, 比如网络请求和文件访问.
    主要线程:

    • UI thread: 控制浏览器上的按钮及输入框
    • Network thread: 处理网络请求, 从网上获取数据
    • Storage thread: 控制文件等的访问
  • 渲染进程 (Renderer Process)
    负责一个 Tab 内的显示相关的工作, 也称渲染引擎.
    默认进程模式: Process-per-site-instance, 每主动打开一个 tab, 开启一个新进程; 页面内通过 js 或者点击链接等形式打开的 tab, 使用同一个进程.
    主要线程:

    • 一个主线程 (main thread)
    • 多个工作线程 (work thread)
    • 一个合成器线程 (compositor thread)
    • 多个光栅化线程 (raster thread)
  • 插件进程 (Plugin Process)
    负责控制网页使用到的插件.

  • GPU进程 (GPU Process)
    负责处理整个应用程序的 GPU 任务.

页面访问过程

  • Step1: UI thread 处理输入的地址 [Browser Process]

  • Step2: UI thread 将处理结果移交 Network thread 发起请求 [Browser Process]
    如果遇到返回 301 重定向响应, 则通知 UI thread 重新处理重定向地址.

  • Step3: Network thread 解读返回 [Browser Process]
    根据 Content-Type 确定类型, 如是 HTML 文件则最终交给 Renderer Process 处理.
    此外, Network thread 还进行 CORB 等安全检查.

  • Step4: Network thread 通知 UI thread 结果, UI thread 去沟通 Renderer Process. [Browser Process]

  • Step5: Renderer Process 从 Browser Process 接收数据进行渲染处理 [Renderer Process]
    Browser Process 向 Renderer Process 发送 IPC 进行确认, 然后发送数据; Renderer Process 收到数据后发送 IPC 告知 Browser Process 渲染开始.
    渲染步骤:

    • Step1: 构建 DOM [main thread]
      构建过程中如果遇到图片, CSS, JavaScript 脚本等资源, 会通知 Browser Process 去请求获取, 并停止 HTML 解析等待资源的加载与执行 (js 资源产生阻塞).
      预加载扫描: 为提高效率, 对 img, link 等标签资源进行预加载,
      异步加载: 对于含有 async 或 defer 等属性的 script 标签, 会进行异步加载, 不阻塞渲染.
      样式计算:对于 style 标签或 link css 资源, 加载 css 代码, 根据 CSS 代码确定每个 DOM 节点的计算样式 (computed style).
    • Step2: 构建布局树 [main thread]
      结合 DOM 树与计算样式, 找到所有元素的几何关系 (每个元素的页面坐标信息及盒子模型大小), 构成布局树 (Render Tree, 也可称渲染树).
      DOM 树中隐藏元素 (display: none) 存在, 伪元素不存在; 布局树中隐藏元素 (display: none) 不存在, 伪元素存在.
    • Step3: 生产绘画记录 [main thread]
      遍历布局树, 获得每个元素的绘制先后顺序.
    • Step4: 合成一次显示结果 [compositor thread]
      光栅化: 把上述信息转化为显示器中的像素.
      Chrome 实行分层合成策略:
      main thread 遍历渲染树来创建一棵层次树 (Layer Tree), 由 raster thread 分层进行光栅化处理 (对于大的层, 再切分成小图块), 处理结果存在GPU Process 的内存中. compositor thread 构建合成, 然后通过 IPC 向 Browser Process 提交渲染结果.
  • Step6: Renderer Process 完成渲染 [Renderer Process]
    当页面渲染完成后(页面及内部的 iframe 都触发了 onload 事件), 向 Browser Process 发送 IPC 消息, 告知渲染完成.

浏览器的事件处理

  • Step1: Browser Process 获得事件发生的类型和发生的位置, 通知 Renderer Process

  • Step2: compositor thread 接收信息, 通知 main thread 进行命中测试 (hit test) 查找事件的目标对象. 如果是非快速滚动区域 (non-fast scrollable region), 则把事件信息发送给 main thread, 等待 main thread 进行事件处理; 如果不是, 则直接合成新的帧.
    命中测试: 遍历绘画记录, 找到包含事件发生坐标的元素
    非快速滚动区域: js 中绑定了事件监听的区域. 全局事件绑定导致整个区域都变成非快速滚动区域, 流畅的合成器独立处理合成帧的模式就失效了 (passive: true 可解决)
    事件优化: 浏览器的渲染频率是每秒 60 帧, 对于如 wheel, mousewheel, mousemove, pointermove, touchmove 等高频触发事件, 浏览器会合并这些连续的事件, 延迟到下一帧渲染时执行 (requestAnimationFrame 之前); 其他非连续性事件则直接派发给 main thread 去执行.

*\ 更详细的信息:
Inside look at modern web browser (part 1)
Inside look at modern web browser (part 2)
Inside look at modern web browser (part 3)
Inside look at modern web browser (part 4)