4
头图

🙋 本文全长 20 分钟 , 市场价值¥ xxx9.9 元, 建议在时间充裕下进行详细阅读。

🈯️ 读本章节内容中,带着以下三个问题进行阅读,更加有助于理解

Problem1 : 打开一个页面需要启动进程的配比是什么?<br/>
Problem2 : 为何浏览器要使用多进程架构设计?<br/>
Problem3 : 多进程架构为何仍会由于单页面卡死导致所有页面崩溃?<br/>

如果想要设计高性能 Web 应用,还是要优化现有的 Web 应用,<br/>
都需要了解浏览器中的网络流程、页面渲染过程,JavaScript 执行流程,以及 Web 安全理论。

在本文中,所有的分析都是基于 Chrome 浏览器的。<br/>
因为 Chrome、微软的 Edge 以及国内的大部分主流浏览器,都是基于 Chromium 二次开发而来;<br/>
再加上 Chrome 是目前世界上使用频率较高的浏览器,所以 Chrome 具有代表性。

🌍 打开一个页面需要启动多少进程?

在开始之前,我们一起看下,Chrome 打开一个页面需要启动多少进程?你可以点击 Chrome 浏览器右上角的“选项”菜单,选择“更多工具”子菜单,点击“任务管理器”,这将打开 Chrome 的任务管理器的窗口,如下图:

chrome 进程图片.jpg

那打开一个页面需要启动 7 个进程?其实不是的。

图片里的扩展程序:Dark Reader 这个是我本机安装的适用于任何网站的黑暗主题扩展程序,所以也占用了进程;<br/>
实用程序:Audio Service 是用于处理音频的,也并非是一定要使用的。<br/>
Storage Service 即使页面未启用 WebStorage, 该进程也会启动并获得内存空间分配, 不过此时 CPU 占用为 0.0

🤔️ 所以打开一个页面基本要使用到的进程配比到底是什么样的?( ⌛️ 请允许我先将这个问题挂起)

在解答这个问题之前,我们需要了解一下进程的概念,因为好多人容易把进程和线程的概念混淆,<br/>
所以这里就将这两个概念以及它们之间的关系一并讲解下。

了解进程和线程

在介绍进程和线程之前,需要先讲解下什么是并行处理。<br/>
如果你理解了并行处理的概念,那么再理解进程和线程之间的关系就会变得轻松许多。<br/>

计算机中的并行处理就是同一时刻处理多个任务。<br/>
<br/>
🌰 举个例子:我们要计算下面这三个表达式的值,并显示出结果<br/>

A = 6+6
B = 6/6
C = 6*6

🚴 正常情况下程序可以使用 单线程 来处理,也就是分四步按照顺序分别执行这 四个 任务<br/>

任务 1 是计算 A=6+6;
任务 2 是计算 B=6/6;
任务 3 是计算 C=6*6;
任务 4 是显示最后计算的结果。

🚗 采用多线程,会怎么样呢?<br/>
我们只需分“两步走”:第一步,使用三个线程同时执行前三个任务;<br/>
第二步,再执行第四个显示任务

通过对比分析,你会发现用 🚴 单线程执行需要四步,而使用 🚗 多线程只需要两步。因此,使用并行处理能大大提升性能。

进程和线程的关系

多线程可以并行处理任务,但是线程是不能单独存在的,它是由进程来启动和管理的。那什么又是进程呢 💡 ?

一个进程就是一个程序的运行实例。(话不多说,上图)

单进程与多进程.jpg

可以看到,线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。

总结来说,进程和线程之间的关系有以下 4 个特点 (🔎 这里不做详细介绍,感兴趣的可以自查资料)<br/>

1.进程中的任意一线程执行出错,都会导致整个进程的崩溃。
2.线程之间共享进程中的数据。(线程之间可以对进程的公共数据进行读写操作)
3.当一个进程关闭之后,操作系统会回收进程所占用的内存。(即使其中任意线程因为操作不当导致内存泄漏,内存也会被正确回收)
4. 进程之间的内容相互隔离。(只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况)

🪐 单进程浏览器时代 VS 多进程浏览器时代

🏎️ 单进程浏览器时代

了进程和线程之后,我们再来一起看下单进程浏览器的架构。。<br/>
顾名思义,单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。

单进程浏览器架构.jpg

如此多的功能模块运行在一个进程里,是导致单进程浏览器不稳定、不流畅和不安全的一个主要因素。<br/>
分析下出现问题的原因<br/>

1:不稳定 ❌<br/>
早期浏览器需要借助于插件来实现诸如 Web 视频等各种强大的功能,但是插件是最容易出问题的模块,并且还运行在浏览器进程之中,所以一个插件的意外崩溃会引起整个浏览器的崩溃。

2:不流畅 ❌<br/>
从上图可以看出,所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的,这就意味着同一时刻只能有一个模块可以执行<br/>

function star() {
  while (true) {
    console.log("什么是快乐星球?");
  }
}
star();

如果创建一个无限循环的脚本,运行在一个单进程浏览器的页面里,你感觉会发生什么?<br/>
因为这个脚本是无限循环的,当其执行时它会独占整个线程,导致其他运行在该线程中的模块就没有机会被执行,结果就是整个浏览器失去响应,变卡顿。

3:不安全 ❌<br/>
这里可以从插件方面来解释该原因。<br/>
插件可以使用 C/C++ 等代码编写,通过插件可以获取到操作系统的任意资源,当你在页面运行一个插件时也就意味着这个插件能完全操作你的电脑。如果是个恶意插件,那么它就可以释放病毒、窃取你的账号密码,引发安全性问题。

以上这些就是单进程架构浏览器的特点,不稳定,不流畅,而且不安全。你可以想象一下这样的场景:当你正在用浏览器打开多个页面时,突然某个页面崩溃了或者失去响应,随之而来的是整个浏览器的崩溃或者无响应,然后你发现你给老板写的邮件页面也随之消失了,这时你的心情会不会和页面一样崩溃呢?

🚀 多进程浏览器时代

多进程浏览器图.jpg

从图中可以看出,最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。

我们来逐个分析下这几个进程的功能。<br/>

1.📒 浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。<br/>

由于进程是相互隔离的,所以当一个页面或者插件崩溃时,影响到的仅仅是当前的页面进程或者插件进程,并不会影响到浏览器和其他页面,这就完美地解决了页面或者插件的崩溃会导致整个浏览器崩溃,也就是不稳定的问题。

不稳定问题得以解决 ✅

2.📒 渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。<br/>

JavaScript 也是运行在渲染进程中的,所以即使 JavaScript 阻塞了渲染进程,影响到的也只是当前的渲染页面,而并不会影响浏览器和其他页面,因为其他页面的脚本是运行在它们自己的渲染进程中的。所以当我们再在 Chrome 中运行上面那个死循环的脚本时,没有响应的仅仅是当前的页面。

不流畅问题得以问题 ✅

出于安全考虑,渲染进程都是运行在沙箱模式下。

可以把沙箱看成是操作系统给进程上了一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取任何数据,例如你的文档和桌面。

Chrome 把插件进程和渲染进程锁在沙箱里面,这样即使在渲染进程或者插件进程里面执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限。

安全问题得以解决 ✅

3.📒 GPU 进程。GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。<br/>

4.📒 网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,后来成为一个单独的进程。<br/>

5.📒 插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。<br/>

6.📒 实用进程 AudioService 是用于处理音频的,也并非是一定要使用的。

7.📒 实用进程 StorageService 是用于处理本地存贮的,包括 Storage(LocalStorage、SessionStorage)、Cache(CacheStorage、ApplicationCache)。

<br/>

✨ ✨ ✨ ✨ ✨ ✨ ✨✨✨✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨✨✨✨<br/>
✨ 讲到这里,就可以回答文章开头提到的前两个问题了 ✨<br/>
✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨ ✨✨✨✨✨✨

<br/>

1. 一个浏览器进程 + 1个GPU进程 + 1个网络进程 + 1个?渲染进程 + 多个插件进程 + StorageService
至少有6个进程启动: 浏览器主进程、GPU进程、NetworkService、当前标签页渲染进程、插件进程、StorageService
如果有音视频场景的话 还需加载 AudioService场景

2. 单进程架构浏览器的特点不稳定,不流畅,不安全,都被多进程架构解决处理 (凡事都有两面性,提升的同时也带来一些问题,更高的资源占用、更复杂的体系架构)。

🌕 多进程为何仍会由于单页面卡死导致所有页面崩溃?

问题讲解之前,要先讲一下打开 Tab 页时 是“1 个”渲染进程 还是“多个”渲染进程?<br/>
<br/>
先了解下什么是同一站点(same-site)。<br/>
具体地讲,我们将“同一站点”定义为根域名(例如,firstleap.cn)加上协议(例如,https:// 或者 http://),还包含了该根域名下...

https://wukong.firstleap.cn
https://www.firstleap.cn
https:///www.firstleap.cn:8080

🆚 Chrome 的默认策略(process-per-site-instance)是每个标签对应一个渲染进程。但如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程。
使用多个进程 还是 1 个 .jpg

🆚 若新页面和当前页面不属于同一站点 , 比如我通过掘金页面里的链接打开 InfoQ 的官网(https://www.infoq.cn/ ), 因为 infoq.cn 和 juejin.cn 不属于同一站点,所以 infoq.cn 会使用一个新的渲染进程。

使用多个进程 还是 2 个 .jpg


总结 :
A: 通常情况下,打开新的页面都会使用单独的渲染进程;
B: 如果从 A 页面打开 B 页面,且 A 和 B 都属于同一站点的话,那么 B 页面复用 A 页面的渲染进程;

因此,当多个页面属于同一站点,共享一个渲染进程,当某个页面崩溃的时候,也将导致同一站点的其他页面也崩溃。

参考文献

https://xie.infoq.cn/article/...


LeapFE
1.1k 声望2.3k 粉丝

字节内推,发送简历至 zhengqingxin.dancing@bytedance.com