原文: Inside look at modern web browser (part 1)
作者: Mariko Kosaka
翻译: kyrieliu
在这个有 4 篇文章的系列中,我们会把 Chrome 浏览器扒个精光 —— 上到浏览器的整体架构,下至页面的渲染规则。如果你对浏览器把代码变成一个具备功能的网站这件事情感到好奇,或者不确定为什么特定的技术会对页面的性能有所改进,那么强烈建议你看完这一系列。
在这篇文章中(Part 1),我将介绍一些核心的计算机术语和 Chrome 的多进程架构。
友情提示:如果你对 CPU/GPU 的概念很熟悉的话,可以直接去看浏览器架构的部分。
为了更好的了解浏览器的运行环境,我们首先需要知道几个计算机的构成部分以及它们分别是做什么的。
首先要知道的概念是中央处理器(Central Processing Unit),也就是我们常说的 CPU。
CPU 可以看作是整个计算机的大脑。在上面这张图中,一个 CPU 核被比喻成了一个工位上的社畜,当有不同的任务传递进来时,它可以一个一个的去处理这些任务。当 CPU 知道如何响应人们的需求时,它几乎可以处理各式各样的任务,比如数学,或是艺术。在早些年,CPU 都是由单芯片所构成。每个核就像是在芯片中又存在着另一个 CPU。现代的电子设备基本上都是多核驱动的,这让人们的手机和电脑都具备了更强的计算能力。
我们需要了解的另一部分,就是图形处理器(Graphics Processing Unit),也就是 GPU。
和 CPU 不同的是,GPU 擅长同时处理跨多核的简单任务。正如它的名字,在设计之初是用来处理图形的。这也正是为什么当图形任务的处理和 GPU 关联起来的时候,通常带来的都是更快速的渲染和更流畅的交互。近年来,随着 GPU 的不断加速和升级,越来越多的计算都可能直接被放在 GPU 上进行了。
当你在手机或电脑上启动一个应用时,CPU 和 GPU 就是为那个应用提供“能量”的两个小兄弟。通常,应用程序通过操作系统提供的“机制”在 CPU 和 GPU 上运行。
在讲解浏览器架构之前,我们还需要了解进程和线程。
进程可以描述为一个应用程序的执行程序。线程则是进程内部用来执行某个部分的程序。
当你启动一个应用时,一个进程就被创建了。程序可能会创建一些线程帮助它完成某些工作,但这不是必须的。操作系统会划分出一部分内存给这个进程,当前应用程序的所有状态都将保存在这个私有的内存空间中。当你关闭应用时,进程也就自动蒸发掉了,操作系统会将先前被占用的内存空间释放掉。
进程可以让操作系统再另起一个进程去处理不同的任务。当这种情况发生时,新的进程又将占据一块内存空间。当两个进程需要通信时,它们可以用一个叫做进程间通讯(Inter Process Communication)的办法解决。许多应用程序都被设计成以这种方式进行工作,所以当其中一个进程挂掉时,它可以在其他进程仍然运行的时候直接重启。
终于要进入这个话题了。所以浏览器是如何通过进程和线程建立起来的呢?有时会是一个进程和多个不同的线程,或是多个进程和少数线程。
这里要注意的一点是,这些不同的体系架构是实现上的细节,至今没有任何一个规范去限制浏览器应该被做成什么样子,不同的浏览器之间的架构可能完全不同。
在这个系列中,我们以 Chrome 的最新架构为准。
首先是浏览器的自身进程,它负责与其他进程协作,主要负责浏览器应用的不同部分,如网络、内存等(这句不好翻,贴上原文:At the top is the browser process coordinating with other processes that take care of different parts of the application)。至于渲染进程,浏览器会为每个窗口分配一个渲染进程。在最近的一次更新中,如果够用的话,Chrome 干脆给每个窗口分配了一个进程;而现在,Chrome 在致力于给每个站点一个独立的进程,包括 iframe。
进程 | 负责些啥? |
---|---|
浏览器进程 | 负责 chrome 的浏览器功能,包括导航栏、书签、后退、前进按钮。当然也负责一些虽然看不到但也很重要的部分,比如网络请求和文件访问。 |
渲染进程 | 窗口内的网站将如何呈现 |
插件进程 | 控制着网站可能用到的所有插件,比如 flash |
GPU 进程 | 处理 GPU 任务,与其他进程隔离。它被划分为不同的进程因为 GPU 会处理来自多个应用程序的请求并将其绘制在同一个平面上。 |
实际上还会有更多的进程,比如扩展进程和工具进程。如果你想看 Chrome 到底会启动多少个进程,打开右上角的菜单,更多工具,接着选择“任务管理器”。这将会打开一个新的窗口,里面是当前正在运行的进程列表,并且会直观的告诉你目前占用了多少 CPU 和内存(Chrome 吃内存的传言真不是盖的)。
之前有提到,Chrome 用的是多进程的渲染方式,最容易想到的场景就是每个窗口(Tab)都有一个独立的渲染进程。假设你打开了三个浏览器窗口,当其中一个窗口因为某种原因崩掉的时候,你大可以直接关闭这个不再响应的窗口并继续你在其他窗口的工作。我们换一个浏览器,所有的窗口都共享同一个进程,当一个窗口挂掉的时候,所有的窗口都直接挂掉了(像不像理财的时候人们总是说:“不要把所有的钱放在同一个钱包里”?)。
将浏览器的工作拆分成不同的进程还有一个好处,就是安全。由于操作系统提供了一种限制进程“权限”的方法,因此浏览器可以将特定的功能和进程有效的隔离开。比如,Chrome 会限制用来处理用户输入的渲染进程去直接访问文件。
每个进程都有各自的内存空间,因此它们常会各自拥有一份基础功能的拷贝。正因为它们之间不像同一进程中的线程那样能够共享资源,所以就需要更多的内存占用。为了节省内存,Chrome 对其自身可调用的进程在数量上做了限制。具体的限制大小在不同性能的机器上各不相同,唯一确定的是,当达到了这个上限后,Chrome 会将同站点的多个窗口交给同一个进程来管理。
浏览器进程也应用了相同的方案。Chrome 正在进行架构层面的整改,目的是将浏览器的各部分功能变成独立的服务,这样就能轻松的将其拆分为不同的进程,也能更加灵活的互相组合。
总的来说,当 Chrome 在较高性能的设备上运行时,它会将每个服务分配至不同的进程,以此来获得更强的运行时稳定性和健壮性;反之,如果 Chrome 运行在一台资源受限的设备上时,Chrome 会将服务整合在一个进程中,以此来节省内存的占用。像这种通过整合进程资源以此来节省内存的手段,已经被用于 Android 上了。
站点隔离是 Chrome 在其 67 桌面版上新增的特性,基本原则是不同的站点各自运行在自己的沙箱环境中,独享进程,并且不允许通信。我们已经讨论过每个窗口一个进程的模型,在这个模型中,浏览器允许跨站点的 iframe 独立进程共享不同站点之间的内存空间。早先在一个渲染进程中(窗口)同时运行 a.com 和 b.com 看起来没有什么问题,因为有同源策略,确保一个站点未经同意就无法访问其他站点的数据。绕过同源策略基本上成为了所有安全攻击的指导方针。而进程间的相互隔离是将站点分开的最佳途径(感兴趣的同学可以去了解一下 Meltdown 和 Spectre 攻击)。
经过多年的工程上的努力,如今的站点隔离已经默认为用户开启了。事实上,站点隔离并不仅仅是为站点分配不同的渲染进程这么简单,它从根本上改变了 iframe 之间的通信方式。打开运行有不同站点 iframe 的开发者工具,意味着浏览器必须做很多看不到的幕后工作,才能让这一切看起来和以前没有什么区别,即使是简简单单的 ctrl+F 在这个场景下也意味着在不同的渲染进程中查询字符串。网上有很多文章介绍浏览器的站点隔离策略,当你看完那些之后就会意识到,为什么站点隔离值得 Chrome 团队为其发布一个独立版本了。
在这篇文章中,我们从宏观的角度了解了浏览器的架构以及这种多进程架构的好处。我们也提到了 Chrome 的服务化以及站点隔离,这些和多进程的架构都有着很深的渊源。
在下一篇文章中,我们会深入了解在为用户呈现一个网站时,这些进程和线程之间会发生什么。
欢迎关注「凯里的前端专栏」,虽然我更新慢,但保证每一篇都是自己用心分享给你。
如果你也喜欢讨论前端技术,或者是对本文/本人有任何建议,非常欢迎加凯里微信好友一起探讨。当然,围绕着前端的任何话题都可以来找我聊~
凯里的微信号是:K-I2ving