深入理解现代浏览器 1: 多进程架构
目录
译注:本文翻译自 ChromeDeveloper 博客, 原文发表于2018年底, 部分特性可能与目前 Chrome 有所不同。建议有条件的读者直接阅读原文。
本系列有4章, 我们将从高层架构到管道渲染的细节来深入理解 Chrome 浏览器。如果你想知道浏览器如何将你的代码变成功能性的网站, 或者你不确实为何被建议使用某种技术来提高网页性能, 那么本系列会适合你。
在本章中, 我们将了解核心计算的术语及Chrome 的多进程架构。
计算机的核心是 CPU 和 GPU
为了理解浏览器运行的环境, 我们需要理解一些计算机部分及它们的作用。
CPU
首先是 中央处理单元(CPU) , CPU可以比作你计算机的大脑。一个CPU核心,就如图中的一个办公室职员, 可以一一处理很多不同的任务。它可以处理从数学到文艺的所有的事件, 甚至知道如何答复客户。过去大部分CPU是单芯的。而核就像生活在同一个CPU中的另一个芯片。在现代硬件中,你通常拥有多核CPU,它为你的手机和笔记本提供更多算力。
GPU
图像处理单元(GPU) 是计算机的另一个部件。和CPU不同的是,GPU擅长处理简单任务, 不过它可以同时跨越多核。顾名思义, 它最初是为处理图像而开发。这就是为何在图形上下文中, "使用GPU" 或 "GPU支持" 往往与快速渲染和平滑交互相关联。现如今, 随着GPU加速运算的发展,越来越多仅在GPU上完成的运算成为可能。
当你在电脑或手机上启动程序时, CPU和GPU都为程序提供算力。通常程序使用操作系统提供的机制在CPU和GPU中运行。
在进程和线程中执行程序
在深入研究浏览器架构之前要掌握的概念是进程和线程。进程可以看作是应用程序的执行程序。而线程存在于进程内部执行其进程程序任意部分。
当你启动应用程序时,一个进程就被创建。此程序可能会创建线程来辅助其工作, 但这不是必须的。操作系统会为进程提供一"板(slab)"内存供其使用,该程序所有的状态都保存于此私有内存空间中。当关闭应用程序时,该进程也随之关闭,操作系统会回收这些内存。
一个进程可以要求操作系统启动另一个进程来完成不同的任务, 此时, 操作系统会为新进程分配另一块内存。如果两个进程需要交流, 它们可以使用 进程间通信(Inter Process Communication IPC) 。很多应用程序都被设计成使用此种方式工作,这样如果一个进程没有响应, 它可以在不停止其它进程的情况下重新启动。
浏览器架构
那么, 如何使用进程和线程来构建浏览器呢?当然, 它可以由一个有很多不同线程的进程构成, 或者是多个使用IPC交流的、有一些线程的多个进程构成。
这里要重点注意的, 是这些不同构架的实现细节 。关没有如何构建浏览器的标准规范, 两种浏览器完全可以使用不同的架构。本文将使用下图来描述 Chrome 当前的架构。
首先是浏览器进程(Browser Process) ,它负责协调应用程序的其它进程。至于渲染进程(Render Process) ,浏览器为会每个标签(tab)创建并分配多个渲染进程。 直到最近, Chrome 还尽可能地为每个标签创建一个进程。目前还试图为每个站点(site) ,甚至 iframe (参见站点隔离)都创建自己的进程,
每个进程都负责什么
下表描述了 Chrome 进程的作用:
- Browser 控制应用程序的"Chrome"部分。 包括地址栏,书签,前进后退按钮,还控制浏览器中一些"不可见"的部分, 如网络请求和文件访问
- Renderer 控制标签内所有的网站的可见部分
- Plugin 控制网站的插件部分,如flash
- GPU 处理独立于其它进程的GPU任务。它独立于其它进程,因为GPU需要处理多个进程的请求却要绘制到同一个 surface里。
还有一些其它进程, 如 Extension Process 和 Utility Process. 如果你想知道你的Chrome里有多少个进程在跑, 你可以打开浏览器菜单,选择 "More Tools", "Task Manager"。
Chrome 中多进程架构的优点
之前我们提到Chrome 使用了多个渲染进程, 在最简单的情况下,你可以想像每个标签都有它自己的渲染进程。假设你打开了3个标签, 每个标签都由一个独立的进程渲染, 如果有一个标签未响应, 你可以关闭未响应的标签, 而保持其他标签正常运行。而如果所有的标签都运行在一个进程中,当一个标签未响应时, 所有的标签都会未响应。那可真蓝瘦。
另一个优点是安全性和 沙盒 sandboxing 。由于操作系统提供了一种限制进程权限的方法, 浏览器可以将某些进程的某些功能沙盒化。例如, Chrome 限制任意用户输入的任意文件访问的进程(如渲染进程)。
因为进程有其私有内存空间,其通常包含了通用基础设施的副本(如 V8,Chrome 的JavaScript 引擎), 这意味着会使用更多的内存, 因为进程不能像同一进程内的线程那样共享内存。为了节省内存, Chrome 对可以使用的进程数量做了限制, 该限制量取决于你的设备上有多少内存及多少CPU算力可供使用, 当进程数达到此限制时, 它开始为访问同一个站点的多个标签使用同一个进程。
节省更多内存 —— Chrome 的服务化
同样的方法适合于 Browser 进程 . Chrome 正在经历架构变更,以将浏览器程序的每个部分作为一项服务运行,从而可以轻松的拆分成不同的进程, 或合并成一个进程。其大意是, 当Chrome 运行在强劲的硬件上运行时,它可以将每个服务拆分为不同的进程, 以提高稳定性。但如果是资源受限的设备, Chrome 会将服务整合到一个进程中以节省内存占用。以此之前,已经在Android 等平台上使用了类似的进程整合方法以减少内存使用。
Per-frame Renderer Processes —— 站点隔离
站点隔离是 Chrome 最近引入的一项功能, 它为每个跨站点 iframe 运行一个独立的 Renderer 进程. 我们之前讨论了每个标签一个渲染进程模型,它允许跨站点的 iframe 运行在同一个渲染进程中,所以不同的站点可以共享内存空间。在渲染进程中运行 a.com 和 b.com 似乎没有问题。同源策略 (Same Origin Policy) 是网络中的核心安全模型, 它确保一个站点不可以未经允许而访问其它站点。绕过此策略是安全攻击的主要目标, 而进程隔离是站点隔离最行之有效的方式。经过 Meltdown/Spectre 漏洞 , 使用进程来实现站点隔离变得尤为明显。自 Chrome 67 以来, 默认在桌面端启动站点隔离。选项卡中的每个跨站点 iframe 都有一个单独的 Renderer Process
启用站点隔离已成为一项历时多年的工程。站点隔离并不像分配不同的渲染进程那般容易, 它从根本上改变了 iframe间相互交流的方式。在一个多iframe的页面上打开开发者工具, 意味着开发者工具必须实现些幕后工作以使其看起来无缝衔接。即使页面上简简单单地运行 Ctrl+F 来查找一个单词, 也意味着跨进程搜索。可见浏览器工程师将跨站点的发布作为一个重要的里程碑的原因。
小节
在本章中, 我们介绍了浏览器架构的高层视图,并介绍了多进程架构的优点, 还介绍了与多进程架构密切相关的Chrome中的服务化和站点隔离。下一章, 我们将开始深入研究当显示一个网页时, 这些进程和线程之间都发生了什么。
喜欢本文吗, 很乐意在本文的评论部分或小蓝鸟 @kosamari 收到您的问题或建议。