[浏览器工作原理]--浏览器的多线程

3,006 阅读7分钟

1.1 进程和线程

1.1.1 并行处理

并行处理可以同一时刻处理多个任务

例如:计算A = 1+2、B = 20/5、C = 7*8 三个表达式的值并显示结果

  • 单线程处理分四步:每计算一个表达式为一步,总共3步,最后显示计算结果为1步 ---- 4步
  • 多线程分2步:第一步,使用三个线程同时执行前三个任务;第二步,再执行第四个显示任务

因此,使用并行处理能大大提升性能

1.1.2 线程VS进程

多线程可以并行处理任务,但是线程是不能单独存在的,它是由进程来启动和管理的

而进程中使用多线程并行处理能提升运算效率,一个进程就是一个程序的运行实例

详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程

1.1.3 进程与线程之间的关系

崩溃

  • 进程中的任意一线程执行出错,都会导致整个进程的崩溃

共享数据

  • 一个进程中的多个线程可以读写进程的公共数据

回收内存

  • 当一个进程关闭,操作系统会回收进程的所占内存

即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收

进程隔离

  • 进程之间的内容是相互隔离的,每一个进程不能访问其他进程的数据

因此一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,可以使用进程间通信(IPC)机制

1.2 单进程浏览器时代

浏览器的所有功能模块都是运行在同一个进程里(包括网络、插件、JavaScript 运行环境、渲染引擎和页面等)

  • 页面线程:页面渲染、页面展示、javascript环境、插件模块
  • 网络线程

如此多的功能模块运行在一个进程里,是导致单进程浏览器不稳定、不流畅和不安全的一个主要因素

  1. 不稳定

    插件是最容易出问题的模块,一个插件的意外崩溃会引起整个浏览器的崩溃,渲染引擎的崩溃也会导致整个浏览器的崩溃,一些复杂的 JavaScript 代码就有可能引起渲染引擎模块的崩溃

  2. 不流畅

    渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中,意味着同一时刻只能有一个模块可以执行;另外,运行一个复杂点的页面再关闭页面,会存在内存不能完全回收的情况,页面的内存泄漏也是单进程变慢的一个重要原因

  3. 不安全

    插件可以使用 C/C++ 等代码编写,通过插件可以获取到操作系统的任意资源,至于页面脚本,它可以通过浏览器的漏洞来获取系统权限

1.3多进程浏览器时代

1.3.1 早期多进程架构

Chrome 的页面是运行在单独的渲染进程中的,同时页面里的插件也是运行在单独的插件进程之中,而进程之间是通过 IPC 机制进行通信

  • 进程是相互隔离的,影响的是当前的页面进程或者插件进程,解决了不稳定问题
  • 即使 JavaScript 阻塞了渲染进程,影响到的也只是当前的渲染页面,同时关闭一个页面,该渲染进程所占用的内存都会被系统回收,解决了不流畅问题
  • 安全沙箱,沙箱里面的程序可以运行,但是不能读写硬盘上任何数据,解决了不安全问题

1.3.2 目前多进程架构

最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程

  • 浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能
  • 渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下
  • GPU 进程。其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了GPU 进程
  • 网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,后来才独立出来,成为一个单独的进程
  • 插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响

带来了一些问题:

更高的资源占用。因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。

更复杂的体系架构。浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了。

1.3.3 未来面向服务架构

为了解决这些问题,在 2016 年,Chrome 官方团队使用“面向服务的架构”(ServicesOriented Architecture,简称SOA)的思想设计了新的 Chrome 架构。也就是说Chrome 整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过 IPC 来通信,从而构建一个更内聚、松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。

灵魂发问

1. 单个页面卡死,导致所有页面崩溃是什么原因?

通常每个标签页面对应一个渲染进程。但是如果从一个页面打开了新页面,两个页面属于同一站点时,那么新页面会复用父页面的渲染进程,因为在一个渲染进程里面,他们就会共享JS的执行环境,也就是说A页面可以直接在B页面中执行脚本。因为是同一家的站点,所以是有这个需求的。因此,此时一个页面崩溃了,会导致同一站点的页面同时崩溃,因为他们共用同一个渲染进程

那什么是同一站点呢?

具体地讲,我们将“同一站点”定义为根域名(例如,geekbang.org)加上协议,还包含了该根域名下的所有子域名和不同的端口,例如

https://time.geekbang.org
https://www.geekbang.org
https://www.geekbang.org:8080

以上都属于同一站点,因为它们的协议都是https,而根域名也都是geekbang.org

2.打开了 2个页面,会有几个进程呢

最新的Chrome浏览器包括至少四个:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程和一个渲染进程,还需要考虑:

  • 页面中有iframe的话,iframe会单独在进程中
  • 有插件的话,插件也会开启进程
  • 多个页面属于同一站点,并且从a打开b页面,会共用一个渲染进程
  • 装了扩展的话,扩展也会占用进程

这些进程都可以通过Chrome任务管理器来查看