
前言
最近看了些浏览器工作原理相关的资料,受益匪浅。聊起如何优化网站,提升性能和体验,相信很多的前端工程师都能够答出一二,譬如减少网络请求、内容压缩、尽量避免操作 DOM 等等。然而,很多的前端工程师知其然却不知其所以然,甚至在很多场景下不知道如何去优化,譬如为什么 HTTP 1.x
推荐使用雪碧图, 而 HTTP 2.0
就不用。生搬硬套式的使用这些优化手段,有时候会适得其反,甚至连自己为何这样做都不知道,仅仅因为优化公式里面有这么一条,我就用了。在我看来,学习及了解底层知识是很有必要的,技术的东西万变不离其宗,懂的原理,上层的东西一通百通了。
出于“知其然知其所以然”的态度,我将会把我对于浏览器工作原理的相关学习进行梳理、提炼和总结,主要目的在于验证自己的学习成果以及理清脉络。相关的参考内容或者学习资料会在文末给出。本文会从 chrome
的分层架构讲起,了解一下浏览器每一层的职责划分。
导读
本文会从这两个方面讲:
- 进程和线程
- 每个时期的浏览器分层。
PART1 进程与线程
为什么说起进程和线程呢?这是因为,浏览器的分层架构和进程及线程原理分不开。关于进程和线程的相关知识点,学习过计算机的都应该了解,这里做一个简单的回顾。
进程
作为资源分配的最小单位
,简单来说,进程就是一个程序的运行实例,当你运行一个程序的时候,系统会为这个程序分配一个单独的内存空间,存放程序代码、运行数据、包括一个主线程,我们把这样一个环境成为进程。
而线程
,是不可以脱离进程而存在的。线程是 CPU 调度的基本单位
,它是比进程更小的能够独立运行的基本单位。一个进程中可以包含 N 多个线程。
1.1 联系及区别
- 线程不可脱离进程而存在,一个进程可以包含多个线程。
- 进程拥有独立的内存空间,而线程共享当前进程的内存空间。
- 一个线程可以创建及销毁另外一个线程,同一个进程中可以存在多个线程并发执行。
- 进程相互隔离,数据共享相对困难,进程间的通信成为
ICP
通信。而线程可以访问同一进程的数据,因此需要考虑上锁情况。 - 不同进程间,一个进程的崩溃不会影响其他进程。一个线程的奔溃会导致该进程的奔溃。
- 进程比线程消耗更多的计算机资源。
- 进程关闭时,系统会回收整块内存。
PART 2 浏览器分层
简单的回顾了下进程与线程,接下来进入了本文的正菜,浏览器的分层架构。
2.1 青铜时代(单进程浏览器)
早在 2007 年以前,市面上的浏览器几乎都是单进程浏览器。单进程如何理解呢?讲白了就是一个浏览器就一个进程,所有的功能模块都跑在单一一个进程中,包括网络、插件、渲染引擎等。图示如下:

在单进程浏览器中,只有一个进程,内部包含程序的数据及文件,以及网络线程、页面线程等各种线程。其中,页面线程任务繁重,不仅要负责页面的渲染和展示,还包含了 javaScript 的执行以及插件的运行。
那么,这样的简单分层架构会有什么问题呢?
-
不稳定
早期的网页可能需要借助各种插件来配合,比如 FLASH。但是插件是很容易出问题的,而插件一旦崩溃,正如前面所说的进程和线程的关系,页面线程的奔溃会导致整个浏览器的奔溃;其次,渲染引擎模块也同样不稳定,比如运行一些 javaScript,程序的出错也会导致线程的奔溃。
-
不流畅
正如上文所说,页面线程任务繁重,不仅仅负责页面的渲染工作,就连插件的脏活累活也都要揽下。由于页面线程只是一个单一线程,同一时间内无法运行多个任务,这就会导致,其中任何一个任务运行所需占用时间直接影响其他任务的执行。举个极端的例子,我们写一段死循环:
while(true){}
这段 javaScript 代码的执行,导致了其他的任务只能等待,并且遥遥无期。可想而知,页面线程任务如此繁重,是非常容易造成卡顿的。
-
不安全
单进程浏览器没有沙箱一说,插件是可以使用 C/C++ 等代码编写的,并且可以随意操作系统,如果当前页面运行着插件时,插件完全可以通过操作电脑,进行释放病毒、盗取账号等攻击;其次,页面脚本也可以通过浏览器的漏洞来获取系统权限,之后对我们的电脑进行恶意的攻击。因此,单进程浏览器存在着不安全的因素。
2.2 白银时代(早期多进程浏览器)
既然单进程浏览器带来了这样那样的各种问题,那么,我们就要去解决它、优化它。而从分层架构方面,解决的方案就是采用多进程的方案。2008年发布的 chrome 就采用了多进程的分层架构。如下图:

在早期的多进程浏览器中,我们已经将浏览器进行一定的拆分,一些功能模块从原来的线程级别提升到了进程级别分别管理。我们分别划分为 浏览器主进程
、多个渲染进程
以及多个插件进程
等,进程之间采用 IPC
的方式进行通信。
其中,浏览器主进程主要负责用户界面交互、下载资源、管理 IPC 以及页面的展示等功能。
插件进程则分别对应每一个插件程序,单独运行互不干扰。
而渲染进程也是相互独立的,一般来说,每一个 Tab 都有可能对应一个渲染进程,渲染进程上进行着页面的解析渲染工作、以及 javaScript 的执行、展示在页面上的图片的合成等等工作。
那么,这么分层有什么好处呢?
事实上,这就解决了先前单进程浏览器的问题:
-
解决不稳定问题
多进程浏览器采用多插件进程、多渲染进程,进程之间彼此隔离,互不干扰。这样的结构就意味着,任何一个插件或者页面崩溃了,都仅仅只会影响当前的进程,浏览器的其他进程安然无恙,浏览器更不会有奔溃的风险。
-
解决不流畅问题
多进程浏览器的渲染进程彼此都跑着自己的程序,互不干扰。也就是说,假设 A、B 页面分别对应着不同渲染进程,A 页面即便是由于各种原因发生了卡顿,B 页面也不会受到影响。并且,关闭页面的时候,对应渲染进程的关闭,浏览器也会回收内存空间,有效避免了内存泄漏的风险。
-
解决不安全问题
正如图中所示,多进程浏览器引入了沙箱(sandbox)的概念,渲染进程和插件进程都运行在沙箱中,被限制了直接读取和写入操作系统的能力,这也避免了程序对操作系统进行恶意的攻击,保障了安全性。
2.3 黄金时代(目前多进程浏览器)
目前,多进程浏览器又有了一些改变,分层更加细了。总体来说,在原来基础上,从浏览器主进程中抽离出了网络进程
、GPU 进程
、V8 代理解析工具
等。

如上图所示,网络进程和 GPU 进程已经独立出来了。事实上,这一点可以通过打开 chrome 浏览器的**“更多工具 -> 任务管理器”**来进行查看,可以看出,打开一个页面,至少需要 4
个进程。

包含一个浏览器主进程、对应标签页的渲染进程、网络进程以及 GPU 进程。其中:
浏览器进程负责用户交互、页面展示、管理子进程等工作。
渲染进程负责界面的渲染解析工作,以及 javaScript 的执行等。
网络进程负责网络请求、下载工作。
GPU 进程最初则是为了 CSS 3D 效果,后面演化为 UI 界面的绘制也都依赖 GPU 进程来进行。
而多出来的一个 V8 代理解析工具,是因为 pac 代理脚本是可以使用 javaScript
进行编写的,那么,解析的工作就放在了浏览器主进程的 V8
上,但是这部分工作放在浏览器主进程上不太好,后面又把这一块单独提了出来,成为一个进程。
多进程浏览器的缺点
多进程浏览器解决了单进程浏览器的三大问题,带来了流畅性、稳定性以及安全性,但同时,缺点也是非常明显的。
首先,每开一个进程都不得不分配公共基础架构的副本,分配一定的内存空间,导致了内存占用非常大,这一点我们也可以通过任务管理器观察到。
其次,这也带来了架构的复杂性。并且各个模块之间耦合程度比较大,容易导致扩展性差。
2.4 钻石时代(面向服务的架构)
为了解决多进程浏览器遗留下来的问题,chrome 在 2016 年使用 SOA
(Services Oriented Architecture,即面向服务的架构) 设计了新一代的 chrome 。
面向服务的基础架构,说的是将原本的各个模块都抽象为服务,每个服务可以运行在单独的进程中,定义好对外的接口,使用 IPC
进行通信,为上层提供服务。

如图示,下层会将浏览器的一些基础功能模块抽象为服务,就好像操作系统一般,为上层去进行服务。这样会达到一种 松耦合、维护性强、可扩展性强的效果。
并且,在计算机资源有限的情况下,浏览器也提供了更加弹性的功能,会灵活的将这些服务整合到一个进程中,节省资源开支。
小结
- 进程是资源分配的最小单位,而线程是 CPU 调度的基本单位。进程包含线程,线程的崩溃影响当前进程,进程的崩溃不影响其他进程,进程间彼此独立。
- 进程拥有自己的内存空间,线程共享当前进程的内存空间。
- 进程间使用
IPC
进行通信。 - 单线程浏览器不稳定、不流畅、不安全。
- 多进程浏览器将模块进行分层,独立出进程来,又引入了
sandbox
,解决了不稳定、不流畅以及不安全问题。 - 面向服务的架构将模块抽象为服务,提供接口,通过
IPC
进行通信,达到了松耦合、高可维护性以及高扩展性的效果。在资源有限的情况下,浏览器会灵活的整合服务到一个进程中,避免资源浪费。
参考资料
Out-of-process V8 proxy resolver
在线直通车
本文首发于:说说浏览器的架构