问题
构建一个永不崩溃或挂起的渲染引擎几乎是不可能的,构建一个绝对安全的渲染引擎也几乎是不可能的。
从某种程度上来说,2006 年左右的网络浏览器状态与过去单用户、协作式多任务操作系统的状况类似。正如在这样的操作系统中,一个行为不端的应用程序可能导致整个系统崩溃一样,网络浏览器中一个行为不端的网页也可能导致整个浏览器和所有当前正在运行的标签页崩溃。只需一个渲染引擎或插件错误,就能导致整个浏览器和所有当前正在运行的标签页崩溃。
现代操作系统更加健壮,因为它们将应用程序置于彼此隔离的独立进程中。一个应用程序的崩溃通常不会影响其他应用程序或操作系统的完整性,并且每个用户对其他用户数据的访问也受到限制。Chromium 的架构旨在实现这种更健壮的设计。
架构概述
Chromium 使用多个进程来保护整个应用程序免受渲染引擎或其他组件中的错误和故障的影响。它还限制每个渲染引擎进程对其他进程以及系统其余部分的访问。从某种程度上来说,这为网页浏览带来了内存保护和访问控制为操作系统带来的好处。
我们将运行 UI 并管理渲染器和其他进程的主进程称为“浏览器进程”或“浏览器”。同样,处理 Web 内容的进程称为“渲染器进程”或“渲染器”。渲染器使用Blink开源布局引擎来解释和布局 HTML。
从图中可以看到:一个渲染进程可能有多个RenderView视图,对应浏览器进程中的RenderViewHost(在灰色阴影区域中标识)。两者以浏览器进程中的I/O线程为桥梁,通过IPC通信。这种结构为一个渲染进程对应的RenderView创建子RenderView提供了可能,例如在cef中,创建子窗体。如果主窗体的标签页关闭了,子窗体也要关闭。
管理渲染器进程
每个渲染器进程都有一个全局RenderProcess对象,用于管理与父浏览器进程的通信并维护全局状态。浏览器RenderProcessHost为每个渲染器进程维护一个对应的对象,用于管理浏览器状态以及与渲染器的通信。浏览器和渲染器使用 Mojo或 Chromium 的传统 IPC 系统进行通信。
管理框架和文档
每个渲染器进程都有一个或多个RenderFrame对象,这些对象对应于包含内容的文档所在的框架。RenderFrameHost浏览器进程中的相应对象管理与该文档相关的状态。每个对象都 RenderFrame被赋予一个路由 ID,用于区分同一渲染器中的多个文档或框架。这些 ID 在一个渲染器内部是唯一的,但在浏览器内部则不同,因此识别一个框架需要同时拥有一个RenderProcessHost和路由 ID。从浏览器到渲染器中特定文档的通信是通过这些RenderFrameHost 对象完成的,这些对象知道如何通过 Mojo 或传统的进程间通信 (IPC) 发送消息。
组件和接口
在渲染器进程中:
- 处理RenderProcessMojo 设置以及浏览器中相应的旧式 IPC RenderProcessHost。每个渲染器进程只有一个 RenderProcess对象。
- 该RenderFrame对象与浏览器进程中的对应对象 RenderFrameHost(通过 Mojo)以及 Blink 层进行通信。该对象表示选项卡或子框架中一个 Web 文档的内容。
在浏览器进程中:
- 该Browser对象代表顶级浏览器窗口。
- 该RenderProcessHost对象表示单个浏览器 ↔ 渲染器 IPC 连接的浏览器端。RenderProcessHost每个渲染器进程在浏览器进程中都有一个对应的对象。
- 该RenderFrameHost对象封装了与的通信 RenderFrame,RenderWidgetHost处理浏览器中的输入和绘画对于RenderWidget的。
有关此嵌入如何工作的更多详细信息,请参阅Chromium 如何显示网页设计文档。(这个单独起一篇文章)
共享渲染器进程
通常,每个新窗口或标签页都会在一个新进程中打开。浏览器会生成一个新进程,并指示其创建一个单独的RenderFrame,这可能会在页面中创建更多 iframe(可能在不同的进程中)。
有时,在标签页或窗口之间共享渲染器进程是必要的或理想的。例如,一个 Web 应用可以用来window.open创建另一个窗口,而新文档如果属于同一源,则必须共享同一个进程。如果进程总数过大,Chromium 也有一些策略可以将新标签页分配给现有进程。这些注意事项和策略在“进程模型”中进行了描述。
检测崩溃或行为异常的渲染器
每个与浏览器进程的 Mojo 或 IPC 连接都会监视进程句柄。如果这些句柄收到信号,则表示渲染器进程已崩溃,受影响的标签页和框架会收到崩溃通知。Chromium 会显示“糟糕的标签页”或“糟糕的框架”图像,通知用户渲染器已崩溃。您可以通过点击“重新加载”按钮或启动新的导航来重新加载页面。当这种情况发生时,Chromium 会注意到渲染器进程已不存在,并创建一个新的渲染器进程。
渲染器沙盒化
由于渲染器在单独的进程中运行,我们可以通过