窥探现代浏览器架构(一)

3,056 阅读12分钟

前言

本文是笔者对Mario Kosaka写的inside look at modern web browser系列文章的翻译。这里的翻译不是指直译,而是结合个人的理解将作者想表达的意思表达出来,而且会尽量补充一些相关的内容来帮助大家更好地理解。

CPU,GPU,内存和多进程架构

在这个4集系列教程里面,我将会从Chrome浏览器的高层次架构(high-level architecture)开始说起,一直深入讲到页面渲染流水线(rendering pipeline)的具体细节。如果你想知道浏览器是怎么把你编写的代码转变成一个可用的网站,或者你不知道为什么一些特定的代码写法可以提高网站的性能的,那你就来对地方了,这篇文章就是为你准备的。

作为本系列文章的第一篇,我们会先了解一些关键的计算机术语以及Chrome浏览器的多进程架构

计算机的核心 - CPU和GPU

要想理解浏览器的运行环境,我们先要搞明白一些计算机组件以及它们的作用。

CPU

首先我们要说的是计算机的大脑 - CPU(Central Processing Unit)。CPU是计算机里面的一块芯片,上面有一个或者多个核心(core)。我们可以把CPU的一个核心(core)比喻成一个办公室工人,他功能强大,上知天文下知地理,琴棋书画无所不能,它可以串行地一件接着一件处理交给它的任务。很久之前的时候大多数CPU只有一个核心,不过在现在的硬件设备上CPU通常会有多个核心,因为多核心CPU可以大大提高手机和电脑的运算能力。

四个CPU核心愉快地在各自工位上一个接着一个地处理交给它们的任务

GPU

图形处理器 - 或者说GPU(Graphics Processing Unit)是计算机的另外一个重要组成部分。和功能强大的CPU核心不一样的是,单个GPU核心只能处理一些简单的任务,不过它胜在数量多,单片GPU上会有很多很多的核心可以同时工作,也就是说它的并行计算能力是非常强的。图形处理器(GPU)顾名思义一开始就是专门用来处理图形的,所以在说到图形使用GPU(using)或者GPU支持(backed)时,人们就会联想到图形快速渲染或者流畅的用户体验相关的概念。最近几年来,随着GPU加速概念的流行,在GPU上单独进行的计算也变得越来越多了。

每个GPU核心手里只有一个扳手,这就说明它的能力是非常有限的,可是它们人多啊!

当你在手机或者电脑上打开某个应用程序的时候,背后其实是CPU和GPU支撑着这个应用程序的运行。通常来说,你的应用要通过操作系统提供的一些机制才能跑在CPU和GPU上面。

计算机的三层架构,最下层是硬件机器,操作系统夹在中间,最上层是运行的应用

在进程和线程上执行程序

在深入到浏览器的架构之前我们还得了解一下进程(process)和线程(thread)的相关概念。进程可以看成正在被执行的应用程序(executing program)。而线程是跑在进程里面的,一个进程里面可能有一个或者多个线程,这些线程可以执行任何一部分应用程序的代码。

进程就像一个大鱼缸,而线程就是浴缸里面畅游的鱼儿

当你启动一个应用程序的时候,操作系统会为这个程序创建一个进程同时还为这个进程分配一片私有的内存空间,这片空间会被用来存储所有程序相关的数据和状态。当你关闭这个程序的时候,这个程序对应的进程也会随之消失,进程对应的内存空间也会被操作系统释放掉。

进程使用系统分配的内存空间去存储应用的数据

有时候为了满足功能的需要,创建的进程会叫系统创建另外一些进程去处理其它任务,不过新建的进程会拥有全新的独立的内存空间而不是和原来的进程共用内存空间。如果这些进程需要通信,它们要通过IPC机制(Inter Process Communication)来进行。很多应用程序都会采取这种多进程的方式来工作,因为进程和进程之间是互相独立的它们互不影响,换句话来说,如果其中一个工作进程(worker process)挂掉了其他进程不会受到影响,而且挂掉的进程还可以重启。

不同的进程通过IPC来通信

浏览器架构

那么浏览器是怎么使用进程和线程来工作的呢?其实大概可以分为两种架构,一种是单进程架构,也就是只启动一个进程,这个进程里面有多个线程工作。第二种是多进程架构,浏览器会启动多个进程,每个进程里面有多个线程,不同进程通过IPC进行通信。

单进程和多进程浏览器的架构图

上面的图表架构其实包含了浏览器架构的具体实现了,在现实中其实并没有一个大家都遵循的浏览器实现标准,所以不同浏览器的实现方式可能会完全不一样。

为了更好地在本系列文章中展开论述,我们主要讨论最新的Chrome浏览器架构,它采用的是多进程架构,以下是架构图:

Chrome的多进程架构图,多个渲染进程的卡片(render process)是用来表明Chrome会为每一个tab创建一个渲染进程。

Chrome浏览器会有一个浏览器进程(browser process),这个进程会和其他进程一起协作来实现浏览器的功能。对于渲染进程(renderer process),Chrome会尽可能为每一个tab甚至是页面里面的每一个iframe都分配一个单独的进程。

各个进程如何分工合作呢?

以下是各个进程具体负责的工作内容:

进程 负责的工作
Browser 负责浏览器的“Chrome”部分, 包括导航栏,书签, 前进和后退按钮。同时这个进程还会控制那些我们看不见的部分,包括网络请求的发送以及文件的读写。
Renderer 负责tab内和网页展示相关的所有工作。
Plugin 控制网页使用的所有插件,例如flash插件。
GPU 负责独立于其它进程的GPU任务。它之所以被独立为一个进程是因为它要处理来自于不同tab的渲染请求并把它在同一个界面上画出来。

不同的进程负责浏览器不同部分的界面内容

除了上面列出来的进程,Chrome还有很多其他进程在工作,例如扩展进程(Extension Process)和工具进程(utility process)。如果你想看一下你的Chrome浏览器现在有多少个进程在跑可以点击浏览器右上角的更多按钮,选择更多工具和任务管理器:

在弹出的窗口里面你会看到正在工作的进程列表,以及每个进程使用的CPU和内存状况。

Chrome多进程架构的好处

那么为什么Chrome会采取多进程架构工作呢?

其中一个好处是多进程可以使浏览器具有很好的容错性。对于大多数简单的情景来说,Chrome会为每个tab单独分配一个属于它们的渲染进程(render process)。举个例子,假如你有三个tab,你就会有三个独立的渲染进程。当其中一个tab的崩溃时,你可以随时关闭这个tab并且其他tab不受到影响。可是如果所有的tab都跑在同一个进程的话,它们就会有连带关系,一个挂全部挂。

不同的tab会有不同的渲染进程来负责

Chrome采用多进程架构的另外一个好处就是可以提供安全性和沙盒性(sanboxing)。因为操作系统可以提供方法让你限制每个进程拥有的能力,所以浏览器可以让某些进程不具备某些特定的功能。例如,由于tab渲染进程可能会处理来自用户的随机输入,所以Chrome限制了它们对系统文件随机读写的能力。

不过多进程架构也有它不好的地方,那就是进程的内存消耗。由于每个进程都有各自独立的内存空间,所以它们不能像存在于同一个进程的线程那样共用内存空间,这就造成了一些基础的架构(例如V8 JavaScript引擎)会在不同进程的内存空间同时存在的问题,这些重复的内容会消耗更多的内存。所以为了节省内存,Chrome会限制被启动的进程数目,当进程数达到一定的界限后,Chrome会将访问同一个网站的tab都放在一个进程里面跑

节省更多的内存 - Chrome的服务化

同样的优化方法也可以被使用在浏览器进程(browser process)上面。Chrome浏览器的架构正在发生一些改变,目的是将和浏览器本身(Chrome)相关的部分拆分为一个个不同的服务,服务化之后,这些功能既可以放在不同的进程里面运行也可以合并为一个单独的进程运行。

这样做的主要原因是让Chrome在不同性能的硬件上有不同的表现。当Chrome运行在一些性能比较好的硬件时,浏览器进程相关的服务会被放在不同的进程运行以提高系统的稳定性。相反如果硬件性能不好,这些服务就会被放在同一个进程里面执行来减少内存的占用。其实在这次架构变化之前,Chrome在Android上面已经开始采取类似的做法了。

Chrome将浏览器相关的服务放在同一个进程里面运行和放在不同的进程运行的区别

单帧渲染进程 - 网站隔离(Site Isolation)

网站隔离(Site Isolation)是最近Chrome浏览器启动的功能,这个功能会为网站内不同站点的iframe分配一个独立的渲染进程。之前说过Chrome会为每个tab分配一个单独的渲染进程,可是如果一个tab只有一个进程的话不同站点的iframe都会跑在这个进程里面,这也意味着它们会共享内存,这就有可能会破坏同源策略。同源策略是浏览器最核心的安全模型,它可以禁止网站在未经同意的情况下去获取另外一个站点的数据,因此绕过同源策略是很多安全攻击的主要目的。而进程隔离(proces isolation)是隔离网站最好最有效的办法了。再加上CPU存在Meltdown和Spectre的隐患,网站隔离变得势在必行。因此在Chrome 67版本之后,桌面版的Chrome会默认开启网站隔离功能,这样每一个跨站点的iframe都会拥有一个独立的渲染进程。

网站隔离功能会让跨站的iframe拥有独立的进程

网站隔离技术汇聚了我们工程师好几年的研发努力,它其实远远没有想象中那样只是为不同站点的iframe分配一个独立的渲染进程那么简单,因为它从根本上改变了各个iframe之间的通信方式。网站隔离后,对于有iframe的网站,当用户打开右边的devtool时,Chrome浏览器其实要做很多幕后工作才能让开发者感觉不出这和之前的有什么区别,这其实是很难实现的。对于一些很简单的功能,例如在devtool里面用Ctrl + F键在页面搜索某个关键词,Chrome都要遍历多个渲染进程去完成。所以我们的浏览器工程师在网站隔离这个功能发布后都感叹这是一个里程碑式的成就。

总结

在本篇文章中,我们探讨了浏览器高层次的架构设计以及多进程架构的带来的好处。同时我们还讨论了服务化和网站隔离这些和浏览器多进程架构息息相关的技术。在下一篇文章中我们要开始深入了解这些进程和线程是如何呈现我们的网站页面的了。

持续关注我的技术动态

我是进击的大葱,关注我和我一起进步成独当一面的全栈工程师!

文章首发于:窥探现代浏览器架构(一)

关注我的个人公众号获取我的最新技术推送!