进程模型
这个文档描述了Chromium支持的不同线程模型,包括它的渲染器进程,以及现有模型实现的问题。
概述
网页内容已经发展到包含大量在浏览器内运行的活跃代码的地步,使得许多网站更像应用程序而非文档。这种变革改变了浏览器的角色,从一个简单的文档渲染器变成一个操作系统。Chromium构建得像一个操作系统那样,使用多进程隔离每个网站和浏览器自身,以一种安全而鲁棒的方式运行这些程序。这提高了鲁棒性,因为每个进程运行在自己的地址空间里,由操作系统调度,即使崩溃也不会互相影响。用户也可以在Chromium的任务管理器里查看每个进程的资源使用情况。
Web浏览器有许多方法可以分割成不同的操作系统进程,最佳的架构的选择取决于许多因素,包括稳定性,资源使用,对实际情况的观察。Chromium支持四种不同的进程模型,允许开发者实验,也有最适合大部分用户的默认模式。
支持的模型
Chromium支持四种不同的模型,它们影响浏览器分配页面给渲染进程的行为。默认情况下,Chromium为用户访问的每个网站使用一个独立的操作系统进程。然而,用户可以在启动Chromium时指定命令行选项,以选择其他的架构:全网站单进程,每组相连标签页一个进程,或者每个东西都放在一个单独的进程中。这些模型的区别在于他们是否影响内容的源,是否影响标签页间的关系,或者两者都会影响。这个章节在更深的细节上讨论每种模型,并在这个文档的后面描述当前Chromium的实现的一些问题。
单网站实例单进程
默认情况下,Chromium为用户访问的每个网站实例创建一个渲染器进程。这保证了不同网站的网页独立渲染,让对同一个网站的不同访问相互独立。因此一个网站实例中的失败(比如,渲染器崩溃)或者重的资源使用不会影响浏览器的其他部分。这个模型基于内容的源和脚本会相互影响的标签页间的关系。因此,两个标签页可以在同一个渲染进程里展示页面,同时在给定的一个标签页中导航到网站外的一个网页,可能切换标签页的渲染进程。(注意,Chromium当前的实现有一些重要的问题,会在下面的Caveat(警告)部分讨论。)
具体说来,我们把一个注册域名(例如:google.com或bbc.co.uk)加一个scheme(例如:https:// )定义为一个网站。这与同源策略定义相似,但它将子域名(比如:mail.google.com和docs.google.com)和端口(比如http://foo.com:8080 )合并到同一个网站中。允许网站的不同子域名或端口中的页面通过Javascript访问是有必要的,如果他们的document.domain变量相同的话,同源策略也会这样允许。
一个网站实例是一些相同网站的相连网页的集合。我们这样认为两个页面是相连的:如果他们可以在脚本代码中获取彼此的引用的话(比如:如果一个页面被另一个页面用Javascript在一个新窗口中打开)。
优点
- 隔离不同网站的内容。这提供了网页内容的命运共享的一种有意义的形式,在这种形式中,网页间的失败不会相互影响。
- 隔离展示相同网站的独立标签页。在不同的标签页中独立访问同样的网站会创建不同的进程。这可以避免同个实例中的争夺与失败,使其不会影响其他实例。
缺点
- 更多的内存负载。在大多数工作负载下,这个模型会比下面的每个网站一个进程创建更多渲染器进程。这虽然能增加稳定性并且增加并发的机会,但它也增加了内存负载。
- 更复杂的实现。不像每个标签页一个进程或者单进程,这个模型需要复杂的逻辑以支持标签在网页间导航时的进程交换,以及代理一些允许的源之间的JavaScript行为,比如传递消息。(关于这个话题的更多内容以及我们正在进行的对这种模型的完全支持的努力,查看下面的Caveats(警告)部分以及我们的站点隔离工程页面。)
单网站单进程
Chromium也支持这样一种进程模式,隔离不同的网站,但将相同网站的所有实例组合到一块。为了使用这个模型,用户需要在启动Chromium时在终端指定 --process-per-site开关。这创建更少的渲染进程,用鲁棒性交换更少的内存占用。这个模型基于内容的源,而非标签页间的关系。
优点
隔离不同网站的内容。正如每个网站实例一个进程的模型那样,不同网站的页面不会共享命运(不会同生共死。。)。
更少的内存占用。这个模型比上一个模型和每个标签一个进程的模型可能创建更少的并行进程。这对于减少Chromium的内存足迹可能是需要的。
缺点
- 可能导致更大的渲染进程。像google.com这样的站点上有着大量的应用程序,它们可能在浏览器里被同时打开,并且全部在同一个进程里渲染。因此,这些应用程序中的资源争夺与失败会影响许多标签页,使得浏览器看起来不能更好地响应。不幸的是,在细粒度上而非通过注册域名区分网站边界,并且不影响向后兼容性是很困难的。
- 实现更加复杂。与每个网站实例一个进程的模型相似,这需要在导航中交换进程以及代理一些javascript操作的逻辑。
单标签页单进程
每个网站或每个网站实例一个进程都需要在创建渲染进程时考虑网站内容的源。Chromium也支持一种简单的模型,将一个渲染器进程分配给每组脚本相关的标签页。这个模型可以使用 --process-per-tab命令行开关来选中。
特别地,我们会把一些脚本相关联的标签页成为一个浏览实例,它也与HTML5范畴中的“一个浏览上下文单元”对应。这个集合由一个标签以及这个标签用javascript代码所打开的标签组合而成。这样的标签必须在同一个进程中渲染,以允许在这些标签页间执行javascript调用(大多数通常发生在同源页面之间)。
优点
- 容易理解。每个标签页分配有一个渲染进程,并不会随时间改变。
缺点
- 导致我们不想要的页面之间命运共享。如果用户在浏览实例中导航一个标签页到一个不同的网站中,新的页面会和其他在同一个浏览实例中的任何其他标签页共享命运。
某些情况下,尽管处于安全的需要,在这个模型中,Chromium仍然强制标签页中的进程交换已经没有什么价值。例如,通常的网页不允许与优先级高的网页(比如设置,或者新标签页)共享进程。因此,这个模型在实践中并没有比单个网站实例单进程更简单。
单进程
最后,出于比较的目的,Chromium支持单进程模型,通过--single-process命令行开关打开。在这个模型中,浏览器和渲染引擎跑在同一个操作系统进程里。
单进程模型提供了一个衡量多进程架构带来的负荷的基线。这不是一个安全的架构,也不是一个鲁棒的架构,因为任何渲染器的崩溃会导致整个浏览器进程挂掉。它只是设计用于测试和开发目的,并且可能包含在其他架构中没有的bug。
沙箱与插件
在每个多进程架构里,Chromium的渲染器进程运行在一个沙箱进程中,它对用户电脑只有有限的访问权限。这些进程对用户的文件系统,显示器,或者大部分其他的资源没有直接的接触。相反,他们只通过浏览器进程获得对允许的资源的访问,而浏览器进程可以在这种访问上附加安全策略。因此,Chromium的浏览器进程可以减轻一个被利用的渲染器引擎能做的事情。
浏览器插件,比如Flash和Silverlight,也在他们自己的进程中执行,并且有些插件,比如Flash运行在Chromium的沙箱中。在Chromium支持的每个多进程架构中,对每种活跃的插件都只有一个进程。因此,所有的Flash实例运行在同一个进程里,不论它们出现在哪个网站或标签页中。
警告
这个部分列出一些Chromium当前进程模型实现的警告,以及它们的意义。
- 大多数始于渲染器的标签页中的导航还没有列入进程交换中。如果用户点击一个链接,提交一个表单,或者被脚本重定向,,如果导航是跨站的话,Chromium不会试图切换标签页中的渲染器进程。Chromium只会为始于浏览器的跨站导航交换进程,比如在地址栏输入一个URL或者打开一个书签。因此,不同网站的页面可能会在同一个进程中渲染,甚至是在单网站实例单进程模型和单网站单进程模型中。这很可能在将来的Chromium版本中,作为网站隔离工程的一部分进行修改。
然而,网页可以使用一种机制来让一个链接指向一个不相关的页面,这样它们可以在不同的进程中安全地渲染。如果一个link有rel=noreferrer或target=blank这样的属性,那么Chromium会在另外的进程中渲染它。
子页面现在是与父页面在相同进程中渲染的。虽然跨站点的子页面没有访问它们的父页面的脚本,而且它们可以在不同的进程中安全地渲染,但Chromium还没有在独立的进程中渲染它们。与上面的第一个警告相似,这意味着不同站点的页面会在同样的进程中渲染。这很可能在Chromium将来的版本中进行修改。
Chromium创建的渲染进程数目有上限。这避免浏览器占用用户电脑的太多进程。这个限制与计算机的内存成比例,并且最多可以有80个进程。因为这样的限制,一个渲染器可能被分配给多个站点。这种重用现在是随机进行的,但将来的版本中,Chromium会做一个启发式的策略,智能的把站点分配给渲染器进程。
实现记录
Chromium中有两个类代表了不同的进程模型实现的抽象需要:BrowsingInstance和SiteInstance。 BrowsingInstance类代表了一个浏览器中脚本相连的集合,在HTML5领域中也被称为相关浏览上下文单元。在单标签页单进程模型中,我们为每个BrowsingInstance创建一个渲染器进程。
SiteInstance类代表了来自相同站点的相同页面。它是BrowsingInstance内部页面的一个子集,因为在BrowsingInstance内部,每个站点只有一个SiteInstance,所以它很重要。在单网站实例单进程模型中,我们为每个SiteInstance创建一个渲染器进程。为了实现单网站单进程,我们必须确保来自同一个站点的所有的SiteInstance归入相同的进程中。
学术论文
现代浏览器架构中隔离Web程序
Charles Reis, Steven D. Gribble (both authors at UW + Google)
Eurosys 2009. Nuremberg, Germany, April 2009.
摘要
今天的许多网站包含大量客户端代码,因此,他们不再是简单的文档,而更多地表现出程序的特征。这给浏览器提出了鲁棒性与性能的挑战。为了给用户提供一个鲁棒而又快速响应的平台,浏览器必须识别应用程序边界,在他们之间提供隔离。
我们在这篇文章中提出三个点。第一,我们为web程序和程序实例提供抽象,并说明这些抽象描绘了浏览器组件是如何交互的,合适的程序边界该如何识别出来;第二,我们识别回退可用性的代价,因为这约束了在不干扰已有网站的情况下,将网页内容划分为程序所能做到的程度;第三,我们展示了一个多进程浏览器架构,相互隔离这些web应用程序,提高容错度,优化资源管理和性能。我们会讨论这个架构在Google Chrome中是如何实现的,然后我们会提供一个量化的性能评估以检查这种架构的好处与代价。
Chromium浏览器安全架构 Adam Barth, Collin Jackson, Charles Reis, and The Google Chrome Team
Stanford Technical Report, September 2008.
摘要
大多数当前网页浏览器使用一个单片架构,将user和web合并到同一个保护域中。如果一个攻击者在这样的浏览器中利用了任意一个代码执行漏洞,他们都可以盗取敏感文件或者安装恶意软件。在这篇文章里,我们会展示Chromium(Google Chrome是在这个开源软件的基础上构建的)的安全架构。Chromium有两个处于不同保护域中的模块:一个是浏览器内核,与操作系统交流,一个是渲染引擎,在沙箱中运行,有着有限的权限。这个架构能够减轻高危攻击,而且不牺牲已有网站的可用性。我们为每个浏览器调用定义了一个威胁模型,并评估这个架构能够如何减少过去的漏洞。