前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解浏览器:Chromium 多进程架构详解

深入理解浏览器:Chromium 多进程架构详解

作者头像
疯狂的技术宅
发布2019-03-27 15:27:10
2.6K0
发布2019-03-27 15:27:10
举报
文章被收录于专栏:京程一灯京程一灯

问题

构建一个从不崩溃或挂掉的渲染引擎几乎是不可能的,构建一个完全安全的渲染引擎也几乎是不可能的。

从某种意义上说,2006 年左右的 web 浏览器就像是过去的单用户多任务的操作系统。在那种老旧的操作系统里,一个异常的应用程序会让整个系统挂掉。而一个异常的 web 页面也可以让整个浏览器崩溃掉,造成崩溃的原因可能仅仅就是一个浏览器或插件的 bug,便摧毁了整个浏览器和当前正在运行的所有标签页。

现代操作系统越来越健壮了,因为它们把应用程序放在了彼此独立的进程中。一个应用程序的崩溃通常不会损害其他应用程序,也不会破坏操作系统的完整性。它也会限制用户访问其他用户的数据。

架构概览

浏览器的一个标签页对应一个独立的进程,这样做是为了保护整个应用程序不受渲染引擎 bug 和故障的影响。我们会限制不同渲染引擎进程之间的彼此访问,也会限制渲染引擎进程对系统其他部分的访问。这样一来,web 浏览器也就有了内存保护和访问控制的机制。

我们把运行UI、管理标签页和插件进程的主进程称为“浏览器进程”或“浏览器”,把特定标签页进程称为“渲染进程”或“渲染器”。渲染进程使用开源布局引擎 Blink 来解释和布局 HTML。

译者注:为了突出“进程”的概念,译文中统一使用“浏览器进程”和“渲染进程”。在日常表述中,用“浏览器”和“渲染器”更多些。

管理渲染进程

每个渲染进程都有一个全局对象 RenderProcess,用来管理与父浏览器进程的通信,同时维护着一份全局状态。浏览器进程为每个渲染进程维护一个 RenderProcessHost对象,用来管理浏览器状态和与渲染进程的通信。浏览器进程和渲染进程使用 Chromium 的 IPC 系统进行通信。

管理视图

每个渲染进程都有一个或多个由 RenderProcess管理的 RenderView对象,它们与内容标签页相对应。相应的,浏览器进程里的 RenderProcessHost管理着 RenderViewHost,它们和渲染进程中的视图相对应。每个视图都有一个视图 ID,用来区分同一个渲染进程里的多个视图。视图 ID 在所在的渲染进程中唯一,但在浏览器进程中不唯一。所以,要标识一个视图就需要 RenderProcessHost和视图 ID。从浏览器进程到特定内容标签页的通信是由 RenderViewHost完成负责的,它知道如何通过 RenderProcessHost将消息发送到 RenderProcess,再到 RenderView

组件和接口

在渲染进程中:

  • RenderProcess处理和浏览器进程中的 RenderProcessHost的 IPC。每个渲染进程只有一个 RenderProcess对象,它处理所有浏览器进程↔渲染进程的通信。
  • RenderView对象负责和浏览器进程中的 RenderViewHost的通信(通过 RenderProcess),也负责和内嵌 WebKit 层进行通信。该对象表示web 标签或弹出窗口的网页内容。

在浏览器进程中:

  • Browser对象表示顶级浏览器窗口
  • RenderProcessHost对象表示浏览器进程↔渲染进程的 IPC 连接的浏览器端。每个渲染进程对应浏览器进程中的唯一 RenderProcessHost
  • RenderViewHost对象封装了与远程 RenderView的通信,RenderWidgetHost 处理 RenderWidget 的输入和绘制

译者注:RenderView 继承自 RenderWidget,RenderViewHost 继承自 RenderWidgetHost

有关此嵌入工作的更详细信息,可参阅 Chromium 如何显示 web 页面的设计文档。

共享渲染进程

通常,每个新窗口/新标签页都会在新进程中打开。浏览器进程会创建一个新的进程,并为它创建一个单独的 RenderView

有时候,有需要/必要在标签页/窗口之间共享渲染进程。比如,Web 应用程序使用 window.open 打开一个期望与之进行同步通信的新窗口,在这种情况下,当创建新窗口/标签页的时候,我们需要复用已打开窗口的进程。如果进程总数太大,或者用户已经把导航到该域的进程打开了,我们也有相应的策略,可以将新标签页分配给已有进程。这些策略在过程模型中有描述。

检测崩溃或异常的渲染进程

每个到浏览器进程的 IPC 连接都会监听进程句柄。如果这些句柄收到了信号,那说明渲染进程已经崩溃了,标签页收到了崩溃通知。目前,我们会显示一个哭脸标签页,以通知用户渲染器已经崩溃。你可以按 reload 按钮重新加载此页面,也可以在此页签中打开一个新导航。当发生这种情况时,我们会创建一个新进程。

沙箱渲染进程

在独立进程中运行渲染进程,我们就可以通过沙箱来限制它对系统资源的访问了。比如,我们通过父浏览器进程确保渲染进程只能访问网络,通过主机操作系统的内置权限来限制它对文件系统的访问。

除了限制渲染进程访问文件系统和网络之外,我们还可以限制它访问用户显示及相关对象。我们在用户不可见的单独 Windows “桌面”上运行每个渲染进程,这样可以防止受影响的渲染进程打开新窗口或捕获按键。

释放内存

渲染进程在单独的进程中运行,这让隐藏标签页拥有较低优先级变得很简单。通常,Windows 的最小化进程是将其内存自动放到“可用内存”池中。在内存不足的情况下,Windows 会把该段内存转移到磁盘上(而不是转移更高优的内存),从而保证用户可见程序的响应。我们可以对隐藏标签页使用相同的策略。当渲染进程没有高级别标签页的时候,我们可以释放该进程的“工作集”,作为给系统的提示,以便在必要时将该段内存交换到磁盘。因为我们发现,当用户在两个标签页之间切换时,工作集大小的减少也会降低标签页切换的性能,所以我们逐渐释放此内存。这意味着如果用户切换回最近使用的标签页,则那个标签页的内存会比最近较少使用标签页更有可能被换入。当有足够的内存来运行所有程序的时候,用户就根本不会注意到这个过程:Windows 只有在需要的时候才会实际收回这些数据。所以当内存充足时,也不会有性能问题。

这有助于我们在低内存情况下获得更优的内存占用。较少使用的后台标签页所对应的内存可以完全交换出来,而前台标签页的数据可以完全加载到内存中。相比之下,如果是单进程的浏览器,它是将所有标签页的数据随机分布在内存中,并且不能清晰地区分出已使用数据和未使用数据,进而浪费内存,影响性能。

插件和扩展

Firefox 风格的 NPAPI 插件在它们自己的进程里运行,与渲染进程不在一块。这在插件架构中有详细描述。

站点隔离项目旨在为渲染进程提供更多的隔离,此项目的一个早期版本可以在隔离进程中运行 Chrome 的 HTML/JavaScript 内容扩展。


往期精选文章

使用虚拟dom和JavaScript构建完全响应式的UI框架

扩展 Vue 组件

使用Three.js制作酷炫无比的无穷隧道特效

一个治愈JavaScript疲劳的学习计划

全栈工程师技能大全

WEB前端性能优化常见方法

一小时内搭建一个全栈Web应用框架

干货:CSS 专业技巧

四步实现React页面过渡动画效果

让你分分钟理解 JavaScript 闭包



小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京程一灯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 架构概览
    • 管理渲染进程
      • 管理视图
      • 组件和接口
      • 共享渲染进程
      • 检测崩溃或异常的渲染进程
      • 沙箱渲染进程
      • 释放内存
      • 插件和扩展
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档