为什么当今的浏览器都是多进程的

157 阅读9分钟

前言

作为一名前端工程师,必不可少的就是与浏览器打交道,然而,对于浏览器的工作原因,不知你是否和我一样一直半解,对于页面的卡顿你是不是还只知道一味地刷新界面而不知其为何卡顿?为什么是进程什么又是线程?一个页面的开启需要多少个进程?如果你也有这样的疑惑,不妨看下去,这篇文章我们就一起了解下浏览器的线程与进程及其关系,以及为什么现在的浏览器都是多进程的,多进程解决了哪些问题。

进程与线程

进程

什么是进程?其实,一个程序就是一个进程,详细的说,启动一个程序之后,操作系统会为这个程序创建一块内存,用来存放代码,运行中的数据以及一个执行任务的主线程,我们把这样的一个运行环境就叫做线程。

线程

线程包含在进程当中,可以理解为同时处理任务数,单线程一次只能处理一个任务,且当前任务终端会影响后面的任务,多线程可以并行处理任务,但是线程不能单独存在,它是由进程来启动和管理的。

线程与进程的特点

1. 进程中的任意线程执行出错,都活导致整个进程的崩溃

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

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

4. 进程之间的内容相互隔离,进程之间数据的通信,需要使用用于进程间通信(IPC)的机制

单进程浏览器

顾名思义,单进程浏览器是指浏览器的所有功能模块都是运行在一个进程里,这些模块包含网络,插件,JavaScript运行环境,渲染引擎和页面等,其实早在2007年以前,市面上的浏览器都是单进程的;以下是单进程浏览器示意图:

image.png

可想而知,这么多模块放在一个进程里,肯定会带来很多问题,接下来我们就来看看单进程会导致哪些问题:

问题1: 不稳定

早期浏览器需要借助于插件来实现诸如Web视频,Web游戏之类等各种强大的功能,但是插件是最容易出问题的模块,并且还运行在浏览器的进程之中,所以一个插件的意外崩溃会引起整个浏览器的崩溃。 除此之外,渲染引擎模块也是不稳定的,通常一些复杂的JavaScript代码就有可能引起渲染引擎的崩溃,和插件一样,渲染引擎的崩溃也会导致整个浏览器的崩溃。

问题二:不流畅

从上面的示意图可以看出,单进程的的所有页面的渲染模块、JavaScript运行执行环境以及插件都是在同一个线程中的,这就意味着同一时间只能有一个模块可以执行。

比如下面这个无线循环的脚本:


function freeze() {
  while (1) {
    console.log("Chrome");
  }
}
freeze();

试想,如果这段代码运行在单进程浏览器的页面里,会发生什么?当这段代码执行时,他会独占整个线程,这样就会导致其他运行在该线程的模块就没有机会被执行。因为浏览器中所有的页面都运行在该线程中,这些页面就没有就会去执行任务,这样就会导致整个浏览器失去响应,变卡顿。

除了上述的脚本和插件之外会让单进程浏览器变卡顿外,页面的内存泄露也是单进程变慢的一个重要原因。通常浏览器的内核都是非常复杂的,运行一个复杂点的页面再关闭页面,会存在内存不能完全回收的情况,这样导致的问题是使用时间越长,内存占用越高,浏览器会变得很慢。

问题3: 不安全

这里依然可以从插件和页面脚本两个方面来解释该原因。

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

至于页面脚本,它可以通过浏览器漏洞来获取系统权限,这些脚本获取系统权限之后也可以对你的电脑做一些恶意的事情,同样也会引发安全问题。

以上这些就是单进程浏览器的特点,不稳定,不流畅,不安全,所以当你在用浏览器的打开多个页面时,很容易因为某个页面的崩溃导致整个浏览器崩溃或者无响应。

多进程浏览器

时至今日,我们使用浏览器打开多个页面时,仍能保持流畅稳定的运行,那是因为从2008年开始,浏览器就进入了多进程时代。我们看看早期的多进程架构,下图是2008Chrome发布时的进程架构:

image.png 从图中可以看出,Chrome的页面时运行在单独的渲染进程中的,同时页面里的插件也是运载在单独的插件进程中,进程之间通过IPC机制进行通信。

接下来我们看看它是如何解决上述单进程的问题的:

解决不稳定的问题

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

解决不流畅的问题

同样,JavaScript也是运行在渲染进程中的,所以即使JavaScript阻塞了渲染进程,也只是影响了当前页面的渲染,并不会影响浏览器和其他页面,因为他们的脚本都是运行在它们的渲染进程中;

对于内存泄露的问题的解决方法就更简单了,因为关闭一个页面,整个渲染进程也会关闭,之后该进程所占的内存都会被回收,这样就轻松解决了浏览器页面的内存泄露问题。

解决不安全的问题

采用多进程架构的好处就是可以使用安全沙箱,你可以把安全沙箱看成是操作系统给进程上的一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取数据,例如你的文档和桌面,Chrome把插件进程和渲染进程都锁在沙箱里面,这样即使在渲染进程或者插件进程里面执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限。

以上便是多进程架构的作用了,不过在经历了不断的更新迭代之后,目前的架构又有了很多新的变化,我们看看下面最新的Chrome多进程架构图:

image.png

从图中可以看出,最新的Chrome浏览器包括:一个浏览器主进程,一个GPU进程,一个网络进程,多个渲染进程和多个插件进程,下面是各个进程的功能介绍:

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

虽然多进程提成了浏览器的稳定性、流畅性和安全性,但是它也带来了一些问题:

  • 更高的资源占用。因为每个进程都会包含公共基础结构的副本,这就意味着浏览器会消耗更多的内存资源。
  • 更复杂的体系架构。浏览器各模块之间耦合性高、拓展性差等问题,会导致现在的架构很难适应新的需求了。

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

总结

最后总结一下本文的内容,最初的浏览器是单进程的,它们不稳定、不流畅、不安全,之后Chrome引入了多进程架构,完美的解决了浏览器的以上问题,我们可以在浏览器中同时开启多个程序或者多个页面并且稳定、流畅、安全的运行。但是、为了支持更多的业务场景,Chrome的架构也变得越来越复杂,变得难以维护,所以未来的浏览器将会是一个面向服务架构的浏览器。