浏览器原理(一):多进程架构

396 阅读8分钟

目前世界上使用率最高的浏览器是 Chrome,它的核心是 Chromium(Chrome 的开发实验版),微软的 Edge 以及国内的大部分主流浏览器,都是基于 Chromium 二次开发而来,它们都有一个共同的特点:多进程架构

当我们用 Chrome 打开一个页面时,会同时启动多个进程,

image

(Chrome 使用了由 Apple 发展来的号称 “地表最快” 的 Webkit 排版引擎,搭载 Google 独家开发的 V8 Javascript 引擎)

高性能的 Web 应用的设计和优化都离不开浏览器的多进程架构,接下来我会以 Chrome 为例,带你了解多进程架构。

进程

介绍多进程架构前,我们先回顾下什么是进程线程

首先,我们知道计算机的核心是 CPU,它承担了所有的计算任务,就像一个时刻运行的工厂,

而进程就好比工厂内的车间,它代表 CPU 可以处理的单个任务,

image

假设工厂电力有限,一次只能给一个车间供电,当这个车间开工时,其他的车间就必须停工,这就好比一个单核 CPU 一次只能处理一个任务。当然,现代的 CPU 通常会使用 时间片轮转调度算法 来实现同时运行多个进程。

进程之间是独立的,在计算机科学领域,一个进程就是一个程序的运行实例

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

那什么又是线程呢?

线程

还是以刚才的工厂车间为例,一个车间里,可以有很多工人,他们可以共享车间的资源,协同来完成一个任务,

而线程就好比车间里的工人,一个进程可以包含多个线程,多个线程共享进程的资源。

如果只有一个工人,那么任务的执行效率是很慢的,所以进程中使用多线程并行处理能提高运算效率

线程不能单独存在,必须由进程来启动和管理,线程和进程有以下一些显著特点,

  1. 进程中的任意一线程执行出错,都会导致整个进程的崩溃
  2. 线程之间可以共享进程中的数据
  3. 当一个进程关闭之后,操作系统会回收进程所占用的内存
  4. 进程之间的内容是相互隔离的

了解了进程和线程后,我们来一起看一下浏览器的进程架构。

单进程架构

2007 年以前,市面上的浏览器基本都是单进程的,浏览器所有的功能模块都是运行在同一个进程,

image

这会导致什么问题呢?

我们知道浏览器的功能模块包括了网络、插件、JavaScript 运行环境、渲染引擎和页面等,如此多的功能模块全部运行在一个进程里,会导致浏览器不稳定、不流畅和不安全

怎么个不稳定?

我们前文提到过,进程中的任意一线程执行出错,都会导致整个进程的崩溃

早期的浏览器许多功能(比如 Web 视频,Web 游戏)都需要借助插件来实现,而插件是最容易出问题的模块,并且还运行在浏览器进程之中,可想而知,一个插件的意外崩溃就会引起整个浏览器的崩溃;此外,渲染引擎模块也是很不稳定的,通常一些复杂的 JavaScript 代码就有可能引起渲染引擎模块的崩溃,进而导致浏览器崩溃,所以单进程架构是不稳定的

为什么不流畅呢?

导致单进程架构不流畅的原因主要有两个,一是所有页面的渲染模块,JavaScript 的执行环境和插件都运行在一个页面线程中,这意味着同一时刻只能有一个模块被执行。想象一下,如果一个耗时的脚本运行在页面线程中,脚本执行时会独占整个线程,导致其他任务都没有机会执行,带来的用户体验就是整个浏览器失去响应,变得卡顿;除此之外,页面的内存泄漏也是单进程变慢的一个原因,我们知道进程关闭时,操作系统会回收进程占用的内存,但是页面是运行在线程中的,当我们运行一个复杂点的页面再关闭页面时,会存在内存不能完全回收的情况。

是什么导致了安全问题呢?

插件和页面脚本是导致安全问题的主要原因。插件可以使用 C/C++ 等代码编写,通过插件可以获取到操作系统的任意资源,这意味着恶意插件的开发成本将大幅降低;此外,页面脚本也可以通过浏览器的漏洞来获取系统权限,引发安全问题。

显然,单进程架构的浏览器由于其不稳定,流畅度差和安全问题,已经逐渐被淘汰,那现代浏览器是如何解决这些问题的呢?

多进程架构

答案就是我们接下来要聊的 “多进程架构” 了。还是以 Chrome 为例,早期的多进程架构第一次出现在 Chrome 中是 2008 年,

为了解决单进程浏览器的问题,Chrome 用独立的渲染进程来运行页面,插件也被放到一个单独的插件进程中执行,浏览器的主线程只负责下载资源,管理进程通信和显示渲染进程生成的页面,我们来简单解释一下多进程架构是如何解决上述问题的,

  • 稳定性:进程间相互隔离通过 IPC 机制进行通信,所有页面或插件的崩溃不会影响到主进程和其他页面
  • 流畅性:1个页面就是1个进程,关闭页面整个进程也关闭了不存在内存泄漏;JavaScript 运行在渲染进程中只会阻塞当前页面
  • 安全性:插件进程和渲染进程被锁进沙箱,进程锁导致即使存在恶意程序也无法突破沙箱去获取系统权限

现代浏览器的发展滚滚向前,Chrome 几乎每六周就会更新一次版本,Chrome 多进程架构相较于之前也有一些新的变化,

演变后的多进程架构将 GPU,网络进程等再次独立出来,不同版本可能会有差异吗,总的来说包括,

  • 1 个浏览器主进程(Browser Process):负责界面显示、用户交互、子进程管理等
  • 多个渲染进程(Renderer):渲染进程负责将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,我们之前提到的 Webkit 排版引擎,JavaScript 引擎都位于渲染进程,默认情况下每打开一个 Tab 页签,Chrome 都会为其创建一个渲染进程
  • 1 个GPU 进程(GPU Process):GPU 最初是为了实现 3D CSS 的效果,现在也用来绘制 UI 界面
  • 1 个网络进程(Network Process):负责页面的网络资源加载
  • 多个插件进程(Plugin Process):主要负责插件的运行,独立是为了防止插件崩溃对浏览器和其他页面造成影响

不过你可能发现,上面的架构和我们开篇用 Chrome 打开一个页面时在 Task Manager 看到的还是不尽相同,拆分后的多进程模型虽然提升了浏览器的稳定性、流畅性和安全性,但还是存在一些问题

  • 资源占用过高,每个进程都包含了公共基础结构的副本(如 JavaScript 运行环境),意味着浏览器会消耗更多的内存资源
  • 架构体系复杂,主要体现在浏览器各模块之间耦合性过高、扩展性较差

为了解决这些问题,Chrome 团队在 2016 年提出了 “面向服务的架构”(Services Oriented Architecture,SOA)思想,

image

通过解耦模块,重构为独立的服务(Service),使每个服务都可以在独立的进程中运行,同时约定服务的访问必须使用定义好的接口,通过 IPC 来通信。在最新的 Chrome 版本中,除了浏览器的主进程,渲染进程和插件进程外,UI、数据库、文件、设备、网络等模块基本都已重构为基础服务(Chrome Foundation Service),这样高内聚,低耦合的设计使得多进程的架构更易于维护和扩展。

同时 Chrome 还提供灵活的弹性架构,在强大性能设备上会以上面我们讲到的面向服务的多进程的方式运行基础服务,但如果在资源受限的设备上,Chrome 会将多个服务整合到一个进程中,以节省内存的占用,

参考链接

写在最后

本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!

如果有疑问或者发现错误,可以在评论区进行提问和勘误,

如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

(完)