这个文档描述了Chromium的顶层架构设计
问题背景
构建一个没有crash和挂起的渲染引擎基本是不可能,构建一个完全安全的渲染引擎也基本是不可能的。
在某些程度上,2006年左右的web浏览器就像过去单用户、多任务合作的操作系统一样。和一些操作系统里发生故障的应用可能搞挂整个系统一样,一个操作不当的web页面也会搞挂整个浏览器。一个页面或者插件的bug就可能把整个浏览器搞崩溃以至于所有页面都不可能用了。
现代操作系统要相对更健壮一些,因为他们把应用都拆分进了独立的进程来相互分隔。一个应用的crash通常不会影响其他应用或者操作系统,并且每个用户和其他用户的数据访问也是有限制的。
架构概述
我们给每个浏览器tab页都使用了独立的进程来保护大部分的应用不受渲染引擎的bug和故障影响。我们也限制来每个渲染引擎进程访问其他渲染引擎进程或者系统的权限。这样在某种程度上就可以让浏览器受益于内存保护对操作系统的访问权限。
我们把运行UI、管理tab和插件进程的主进程称为“browser process”或者“broswer”。同样地,具体的tab进程被称为“render process”或者“renderer”。renderer用Blink这个开源布局引擎来解析HTML布局。
管理渲染进程
每个渲染进程都有一个全局的RenderProcess对象来管理与父browser进程的通信,并且维护全局状态。browser维护每个渲染进程对应的RenderProcessHost,来管理browser状态和渲染器的通信。browser和renderers通过Chromium IPC系统进行通信。
管理视图
每个渲染进程都有一个或者更多的RenderView对象,由RenderProcess管理,对应每个tab的内容。对应的RenderProcessHost维护一个RenderViewHost对应每个renderer的视图。每个视图都被分配到一个ID来在同一个renderer里区分多个view。这些ID在同一个renderer里都是唯一的,但是在browser里面不一定唯一,所以识别一个view同时需要RenderProcessHost和view ID。browser和某个具体的tab内容之间通过RenderViewHost对象,通过RenderProcessHost给RenderProcess 再定位到RenderView发送信息来进行通信。
组件和接口
在渲染进程中:
- RenderProcess处理IPC和browser进程中对应RenderProcessHost之间的通信。每个渲染进程中都明确地有一个RenderProcess对象。这就是browser ↔ renderer之间通信的方式。
- RenderView对象和browser进程中对应的RenderProcessHost进行通信(通过RenderProcess),和我们嵌入式的WebKit层。这个对象代表来一个web页面的内容或者一个弹出的窗口。
在浏览器进程中:
- Browser对象代表一个顶层的浏览器窗口
- RenderProcessHost对象代表浏览器侧的一个browser ↔ renderer通信IPC连接。每个渲染进程和浏览器进程之间都有一个RenderProcessHost对象。
- RenderViewHost对象包含了和远端RenderView之间的通信,RenderWidgetHost处理输入并绘制浏览器中的RenderWidget
内部更多详细的工作原理可以,可以查看 How Chromium displays web pages)设计文档
共享渲染进程
通常,每个新的窗口或者tab都会在一个新的进程中打开。浏览器会打开一个新的进程并且创建一个单独的RenderView。
但是有的时候,在tab和window之间共享渲染进程也是必要的。一个web饮用会打开一个新的窗口并且希望能同步的进行通信,比如,在Javascript中使用window.open。在这种情况下,当我们创建一个新的window或者tab的时候,我们就需要重复利用之前window已经打开过的进程。当进程数太多的时候我们也有一些策略来分配已经打开的渲染进程给新的tab,或者如果这个用户已经有打开过这个域名下的页面创建的进程的时候。这个策略在Process Models中。
检测crash或者故障的renderer
每个和browser进程通信的IPC连接都会监听process handler。如果这些handler收到消息,renderer进程有crash,那么这些tab就会被通知到crash。当前,我们会展示一个“sad tab”到屏幕上来告诉用户渲染进程crash了。这个页面可以通过按下刷新按钮重新加载。这个时候,我们也会通知没有进程了,需要重新创建一个进程。
渲染沙箱
针对一个独立的进程,我们通过沙箱去限制访问系统资源的权限。比如,我们可以通过父browser进程来保证渲染器只能访问网络。同样的,我们也可以用操作系统内置的权限来限制它访问文件系统的权限。
除了限制渲染器访问文件系统和网络的权限,我们还可以限制访问用户展示相关的权限。我们运行渲染进程在一个用户不可见的独立Windows Desktop上。这防止了渲染进程打开一个新的window或者采集按键。
回收内存
让渲染器在一个独立的进程中运行,很自然的就会给隐藏的tab更低一点的优先级。通常,被降低内存的进程会自动地把内存分配到一个“可用内存”的池子里。在低内存模式下,Windows会在要切换到高优先级模块内存之前把这些内存交换到硬盘上,来使用户可见的程序能更快的响应。我们可以应用同样的策略到隐藏的tab里。当一个渲染进程没有最高优先级的tab时,需要时我们可以把这个进程的“working set”大小从系统内存先释放到硬盘上。因为我们发现减少工作区域大小也会降低用户从2个tab之间切换的切换性能。但是当用户有足够的内存来运行他们的程序的时候就不会通知这个进程:Windows只有在确实需要的时候才会回收这些数据,所以当有足够的内存时不会有性能的损失。
插件和扩展
Firefox风格的NPAPI插件运行在他们自己的进程里,和renderer分离开,详细的描述在plugin architecture里。
site isolation项目提供了renderer之间更多的隔离,包括了Chrome的HTML/Javascript在独立进程中的上下文扩展。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。