除了想入门浏览器内核开发的读者之外,这篇文章对于CEF(Chromium Embedded Framework)开发者也有一定的参考意义。CEF是一个将Chromium浏览器引擎嵌入到其他应用程序的框架。了解Chromium的高级架构及其如何将其划分为多个进程类型,对于CEF开发者来说非常有帮助。通过了解Chromium的架构,开发者可以更好地理解CEF如何运作,并在开发过程中作出更明智的决策。本文讨论了Chromium的多进程架构、进程间通信、渲染器沙盒化和内存管理等概念,这些概念在使用CEF时同样适用。
原文地址:https://www.chromium.org/developers/design-documents/chromeviews/
多进程架构
本文档描述了Chromium的高级架构以及如何在多个进程类型之间进行划分。
问题
构建一个永不崩溃或挂起的渲染引擎几乎是不可能的。同样,构建一个完全安全的渲染引擎也几乎是不可能的。
在某种程度上,2006年左右的网络浏览器的状态类似于过去单用户、协同多任务操作系统的状态。在这样的操作系统中,一个行为不端的应用程序可能导致整个系统崩溃,而一个行为不端的网页在网络浏览器中也可能如此。一个渲染引擎或插件错误就足以使整个浏览器和所有正在运行的标签页崩溃。
现代操作系统更加健壮,因为它们将应用程序放入相互隔离的单独进程中。一个应用程序的崩溃通常不会影响其他应用程序或操作系统的完整性,并且每个用户访问其他用户数据的权限受到限制。Chromium的架构旨在实现这种更强大的设计。
译者注语:“多进程模型在不可避免地 一些问题和复杂性的同时,也带来了了更多的优势,而且这些优势非常的重要。该模型至少有三点好处:其一是避免因单个页面的不响应或者崩溃而影响整个浏览器的稳定性,特别是对用户界面的影响;其二是,当第三方插件崩溃时不会影响页面或者浏览器的稳定性,这时因为第三方插件也被使用单独的进程来运行;其三是,它方便了安全模型的实施,也就是说沙箱模型是基于多进程架构的。——《Webkit技术内幕》”
架构概述
Chromium使用多个进程来保护整个应用程序免受渲染引擎或其他组件的错误和故障影响。它还限制了每个渲染引擎进程对其他进程和系统其余部分的访问。在某些方面,这为网络浏览带来了操作系统在内存保护和访问控制方面的优势。
我们将运行UI并管理渲染器和其他进程的主进程称为“浏览器进程”或“浏览器”。同样,处理Web内容的进程称为“渲染器进程”或“渲染器”。渲染器使用Blink开源布局引擎来解释和布局HTML。
![](https://img.haomeiwen.com/i13248131/28f29bd23cc7c846.png)
译者注语:Chromium允许用户配置Renderer进程被创建的方式,默认情况下,Chromium为每个标签页都创建一个独立的渲染进程,而不管它们是否是不同域不同实例。
管理渲染器进程
每个渲染器进程都有一个全局的RenderProcess对象,该对象负责与父浏览器进程通信并维护全局状态。浏览器为每个渲染器进程维护一个相应的RenderProcessHost,用于管理渲染器的浏览器状态和通信。浏览器和渲染器使用Mojo或Chromium的遗留IPC系统进行通信。
译者注语:“Renderer进程在网页的加载过程中需要获取资源,但是由于安全性(实际上,当沙箱模型打开的时候,Renderer进程是没有权限去获取资源的)和效率上(资源共享等问题)的考虑,Renderer进程的资源获取实际上是通过进程间通信将任务交给Browser进程来完成,Browser进程有权限从网络或者本地获取资源。——《Webkit技术内幕》”
管理帧和文档
每个渲染器进程都有一个或多个RenderFrame对象,这些对象对应于包含内容的文档帧。浏览器进程中相应的RenderFrameHost负责管理与该文档相关的状态。每个RenderFrame被赋予一个路由ID,用于区分同一渲染器中的多个文档或帧。这些ID在渲染器内部是唯一的,但在浏览器内部不是唯一的,因此识别帧需要一个RenderProcessHost和一个路由ID。从浏览器到渲染器中特定文档的通信是通过这些RenderFrameHost对象完成的,这些对象知道如何通过Mojo或遗留IPC发送消息。
组件和接口
在渲染器进程中:
- RenderProcess处理与浏览器中相应的RenderProcessHost之间的Mojo设置和遗留IPC。每个渲染器进程中恰好有一个RenderProcess对象。
- RenderFrame对象与其在浏览器进程中的相应RenderFrameHost(通过Mojo)以及Blink层进行通信。此对象表示标签页或子框架中的一个Web文档的内容。
在浏览器进程中:
- Browser对象表示一个顶级浏览器窗口。
- RenderProcessHost对象表示单个浏览器↔渲染器IPC连接的浏览器端。每个渲染器进程在浏览器进程中都有一个RenderProcessHost。
- RenderFrameHost对象封装了与RenderFrame的通信,RenderWidgetHost处理浏览器中RenderWidget的输入和绘制。
有关这种嵌入如何工作的更详细信息,请参阅Chromium如何显示网页设计文档。
共享渲染器进程
通常,每个新窗口或标签页都会在一个新进程中打开。浏览器将生成一个新进程,并指示它创建一个单独的RenderFrame,该RenderFrame可能在页面中创建更多的iframe(可能位于不同的进程中)。
有时,有必要或者需要在标签页或窗口之间共享渲染器进程。例如,Web应用程序可以使用window.open创建另一个窗口,如果新文档属于相同的源,则必须共享相同的进程。当总进程数量过大时,Chromium还有将新标签页分配给现有进程的策略。这些注意事项和策略在进程模型中有详细描述。
检测崩溃或行为异常的渲染器
每个与浏览器进程的Mojo或IPC连接都会监视进程句柄。如果这些句柄被触发,则渲染器进程已经崩溃,受影响的标签页和帧将被通知崩溃。Chromium显示一个“悲伤的标签页”或“悲伤的帧”图像,通知用户渲染器已崩溃。用户可以通过按重新加载按钮或开始新的导航来重新加载页面。发生这种情况时,Chromium会注意到没有渲染器进程并创建一个新的渲染器进程。
对渲染器进行沙盒化
由于渲染器在单独的进程中运行,我们有机会通过沙盒化来限制其对系统资源的访问。例如,我们可以确保渲染器只能通过Chromium的网络服务访问网络。同样,我们可以使用主机操作系统的内置权限来限制其对文件系统的访问,或者限制其对用户显示和输入的访问。这些限制显著降低了受损渲染器进程所能完成的事情。
回收内存
由于渲染器在单独的进程中运行,将隐藏的标签页视为较低优先级变得非常简单。通常情况下,Windows上的最小化进程会自动将其内存放入“可用内存”池中。在低内存情况下,Windows会在将高优先级内存交换出去之前,将这些内存交换到磁盘上,以保持用户可见程序的响应性。我们可以将这个原则应用到隐藏的标签页。当一个渲染进程没有顶级标签页时,我们可以释放该进程的“工作集”大小,作为一个提示,让系统在需要时优先将该内存交换到磁盘上。我们发现减小工作集大小也会降低用户在两个标签页之间切换时的性能,所以我们会逐步释放这些内存。这意味着,如果用户切换回最近使用过的标签页,该标签页的内存比较旧的标签页更有可能已经加载到内存中。对于内存足够运行所有程序的用户,他们不会注意到这个过程:Windows只有在需要时才会实际回收这些数据,所以在内存充足的情况下不会有性能损失。
这有助于我们在低内存情况下获得更优的内存占用。不常用的后台标签页的内存可以完全交换出去,而前台标签页的数据可以完全加载到内存中。相比之下,单进程浏览器中所有标签页的数据都会随机分布在其内存中,无法如此干净地区分使用和未使用的数据,既浪费内存,也影响性能。
其他进程类型
Chromium还将许多其他组件分割成单独的进程,有时以平台特定的方式。例如,它现在有单独的GPU进程、网络服务和存储服务。沙盒化的实用程序进程也可用于较小或高风险的任务,作为满足安全性的两项规则之一的方式。
网友评论