问题
构建几乎不会崩溃或挂起的渲染引擎几乎是不可能的。构建完全安全的渲染引擎几乎也是不可能的。
在某种程度上,2006年左右的网络浏览器的状态类似于过去的单用户,协作多任务操作系统。由于此类操作系统中的行为异常的应用程序可能会破坏整个系统,因此Web浏览器中的行为异常的网页也可能因此而崩溃。它只需要一个浏览器或插件错误即可关闭整个浏览器和所有当前运行的选项卡。
现代操作系统更强大,因为它们将应用程序置于相互隔离的单独进程中。一个应用程序中的崩溃通常不会损害其他应用程序或操作系统的完整性,并且每个用户对其他用户数据的访问受到限制。
建筑概述
我们对浏览器选项卡使用单独的过程,以保护整个应用程序免受渲染引擎中的错误和干扰。我们还限制了每个渲染引擎进程对其他进程以及系统其余部分的访问。在某些方面,这为Web浏览带来了内存保护和访问控制带给操作系统的好处。
我们将运行UI并管理选项卡和插件过程的主要过程称为“浏览器过程”或“浏览器”。同样,特定于选项卡的过程称为“渲染过程”或“渲染器”。渲染器使用Blink开源布局引擎来解释和布局HTML。
管理渲染过程
每个渲染进程都有一个全局RenderProcess
对象,该对象管理与父浏览器进程的通信并维护全局状态。浏览器RenderProcessHost
为每个渲染进程维护一个对应项,该进程管理浏览器状态和渲染器的通信。浏览器和渲染器使用Chromium的IPC系统进行通信。
管理视图
每个渲染过程都有一个或多个RenderView
对象,由管理RenderProcess
,这些对象对应于内容的选项卡。对应项 在渲染器中RenderProcessHost
维护RenderViewHost
与每个视图对应的内容。每个视图都有一个视图ID,该ID用于区分同一渲染器中的多个视图。这些ID在一个渲染器中是唯一的,但在浏览器中不是唯一的,因此要识别视图,需要一个RenderProcessHost
ID和一个View ID。通过这些RenderViewHost
对象可以完成从浏览器到特定内容选项卡的通信,这些对象知道如何将消息通过它们发送RenderProcessHost
到RenderProcess
和RenderView
。
组件和接口
在渲染过程中:
- 在浏览器中
RenderProcess
使用相应的句柄处理IPCRenderProcessHost
。RenderProcess
每个渲染过程只有一个对象。这就是所有浏览器↔渲染器通信的发生方式。 - 该
RenderView
对象RenderViewHost
在浏览器进程中(通过RenderProcess)和我们的WebKit嵌入层与其对应的对象通信。此对象表示选项卡或弹出窗口中一个网页的内容
在浏览器过程中:
- 该
Browser
对象表示顶级浏览器窗口。 - 该
RenderProcessHost
对象表示单个浏览器↔渲染器IPC连接的浏览器端。有一个RenderProcessHost
在每个渲染过程中的浏览器进程。 - 该
RenderViewHost
对象封装的通信与远程RenderView
和RenderWidgetHost处理用于输入和绘画RenderWidget在浏览器中。
有关此嵌入的工作方式的更多详细信息,请参阅Chromium如何显示网页 设计文档。
共享渲染过程
通常,每个新窗口或选项卡都会在新过程中打开。浏览器将产生一个新进程,并指示它创建一个RenderView
。
有时有必要或希望在选项卡或窗口之间共享渲染过程。Web应用程序会打开一个新窗口,希望与之进行同步通信,例如,使用 JavaScript中的window.open。在这种情况下,当我们创建新的窗口或选项卡时,我们需要重用打开窗口的过程。如果进程总数太大,或者用户已经打开一个导航到该域的进程,我们还可以为新进程分配新选项卡的策略。这些策略在过程模型中进行了描述。
检测崩溃或行为异常的渲染器
与浏览器进程的每个IPC连接都会监视进程句柄。如果发出了这些句柄的信号,则表示渲染过程已崩溃,并且向选项卡通知了崩溃。现在,我们显示一个“悲伤的标签”屏幕,通知用户渲染器崩溃了。可以通过按下重新加载按钮或开始新的导航来重新加载页面。发生这种情况时,我们注意到没有任何流程,而是创建一个新的流程。
沙盒渲染器
鉴于渲染器在单独的进程中运行,我们有机会通过sandboxing限制其对系统资源的访问。例如,我们可以确保渲染器对网络的唯一访问是通过其父浏览器进程进行的。同样,我们可以使用主机操作系统的内置权限来限制其对文件系统的访问。
除了限制渲染器对文件系统和网络的访问之外,我们还可以限制对渲染器对用户的显示和相关对象的访问。我们在用户看不见的单独Windows“ 桌面 ” 上运行每个渲染过程。这样可以防止受损的渲染器打开新窗口或捕获击键。
回馈记忆
给定渲染器在单独的进程中运行,将隐藏的选项卡视为较低优先级就变得很简单。通常,Windows上最小化的进程会将其内存自动放入“可用内存”池中。在低内存情况下,Windows将在交换出更高优先级的内存之前将该内存交换到磁盘上,从而有助于使用户可见的程序具有更高的响应速度。我们可以将相同的原理应用于隐藏的标签。当渲染进程没有顶级选项卡时,我们可以释放该进程的“工作集”大小,以提示系统在必要时先将该内存换出到磁盘。因为我们发现减小工作集大小也会降低用户在两个选项卡之间切换时的选项卡切换性能,所以我们逐渐释放此内存。这意味着,如果用户切换回最近使用的选项卡,则与最近使用的选项卡相比,该选项卡的内存更有可能被分页。具有足够内存来运行所有程序的用户根本不会注意到此过程:Windows仅会在需要时才真正回收这些数据,因此,如果有足够的内存,则不会对性能造成任何影响。
这有助于我们在低内存情况下获得更好的内存占用。与很少使用的背景标签相关联的内存可以完全换出,而前景标签的数据可以完全加载到内存中。相比之下,单进程浏览器会将所有选项卡的数据随机分布在其内存中,并且不可能如此干净地分离使用和未使用的数据,从而浪费内存和性能。
插件和扩展
Firefox风格的NPAPI插件在其自身的进程中运行,与渲染器分开。插件体系结构中对此进行了详细描述。