Chrome架构

1,169 阅读7分钟

先从资源管理器认识Chrome

首先我们找到资任务管理器:

Snipaste_2021-04-14_16-57-16.png

打开后我们会看到这样的一幕:

Snipaste_2021-04-14_16-55-17.png

不知道大佬们有没有发现, 虽然我们之打开了一个Tab页, 但是Chrome竟然启动了个进程!!!

  1. 浏览器主进程;
  2. GPU进程;
  3. Network Service;
  4. Storage Service;
  5. Audio Service;
  6. 插件进程(6 - 8);
  7. 标签页进程;

是不是看起来和Windows任务管理器一样? 当然Chrome的任务管理器也是用来管理浏览器的进程信息的, 那么问题来了, 为什么要启动这么多进程呢?

首先回答这个问题之前, 我们来复习一下什么叫做 进程与线程

进程与线程

维基百科:

进程(英语:process),是指计算机中已运行的程序。进程曾经是分时系统的基本运作单位。在面向进程设计的系统(如早期的UNIX,Linux 2.4及更早的版本)中,进程是程序的基本执行实体;在面向线程设计的系统(如当代多数操作系统、Linux 2.6及更新的版本)中,进程本身不是基本运行单位,而是线程的容器。

线程(英语:thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

是不是看起来有点抽象? 那么我换一种方式解释一下吧!

举个栗子O(∩_∩)O~

A = 1 + 2
B = 3 + 4
C = 5 + 6
........

下面问题来了, 如果想获得a, b, c三个变量的结果, 程序会怎样执行?

如果是单线程的, 会按照顺序计算a -> b -> c -> result最后得出结果, 也就是说会把过程拆解成为4个任务:

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

这就是单线程的处理逻辑.

如果是多线程呢就会简单的多, 首先会在该应用的进程中启动三个线程, 三个线程分别被CPU调度, 同时进行运算, 最后选中一条复用其中一条线成去显示result;

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

我们先来一张高清无码大图供大家欣赏:

3380f0a16c323deda5d3a300804b95da.png

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

进程与线程之间的关系

总的来说呢有一下四个特点吧:

  1. 一个进程中, 任意一个线程出错都会导致进程崩溃.

这个很好理解, 如果上面的变量A运算出错的话, 其他几个运算的正确也不会得出预期的结果, 所以就会导致整个进程的崩溃.

  1. 线程之间共享进程中的数据

如图: d0efacd7f299ed99e776cb97da2a799e.png从上图可以看出,线程 1、线程 2、线程 3 分别把执行的结果写入A、B、C中, 然后线程 2 继续从A、B、C中读取数据,用来显示执行结果.

  1. 当一个进程关闭之后,操作系统会回收进程所占用的内存

当任何一个进程退出的时候, 系统都会回收改线程所申请的所有资源, 无论垓进程是否存在过内存泄漏之类的骚操作, 都会一并回收;

  1. 进程之间的内容相互隔离

进程隔离是为保护操作系统中进程互不干扰的技术, 每一个进程只能访问自己占有的数据, 也就避免出现进程 A 写入数据到进程 B 的情况. 正是因为进程之间的数据是严格隔离的, 所以一个进程如果崩溃了, 或者挂起了, 是不会影响到其他进程的. 如果进程之间需要进行数据的通信,这时候线程之间也会有通信机制. 例如: 如果我们浏览器中一个页面写了一个死循环, 不会导致整个浏览器崩溃, 而只会在关闭这个页签之后, 其他页面还是可以正常运行的.

早期浏览器架构:

6ddad2419b049b0eb2a8036f3dfff1ca.png

其实早起的浏览器呢是个单进程的架构, 所有的页面渲染、 页面展示、插件、 js运行和插件都在页面线程中运行, 这也就会导致页面加载很慢, 由于执行的任务较多, 很容易导致内存溢出和程序崩溃;

例如如果由于一个插件除了意也会导致页面的崩溃.

再有就是很不流畅, 从上面的“单进程浏览器架构示意图”可以看出, 所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的, 这就意味着同一时刻只能有一个模块可以执行.

最最重要的就是很不安全, 由于我们插件等模块可以用c++ 等代码编写, 极有可能调用系统功能, 访问系统资源, 也有可能会给页面植入一些不安全的脚本或者篡改请求一类的恶心人的骚操作.

多进程浏览器时代来临了

按照惯例先上图:

b61cab529fa31301bde290813b4587fc.png

从图中我们可以看出, 浏览器把每一个功能模块拆解成了独立的进程去管理(隔离).

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

讲到这里细心地小伙伴或许会注意到, 最开始截图中有几个实例程序, 名称后面都有Service, 前面有Network/Storage/Audio.

那么这个又是什么呢?

这个是2016年Chrome团队使用面向服务架构(Services Oriented Architecture,简称 SOA),这个设计也就是说Chrome整体架构朝向现代操作系统所采用的"面向服务的架构"方向发展了, 原来每个模块独立成服务(Service), 每个服务都是独立运行的, 访问服务的时候, 必须定义好结构, 服务和服务之间通过IPC进程通信, 从而构建了一个更聚合、松耦合、易于维护的拓展系统.

Chrome 最终要把 UI、数据库、文件、设备、网络等模块重构为基础服务, 类似操作系统底层服务, 下面是 Chrome“面向服务的架构”的进程模型图:

11111111111.png

总结

其实我觉得现在的Chrome更像是一个操作系统, Chrome整一个越来越完善的方向发展, 更加的合理讲究效率, 从单进程到现在的多进程, 到最后的面向服务架构的设计, 让我们祝福Chrome越来越好吧.