admin管理员组

文章数量:1530517

一、序言

其实做了两年前端,一直都是从代码和网络方面考量问题,一直都没有考虑过跟用户打交道最近的其实是浏览器。浏览器这个东西怎么说呢,从刀耕火种时代的ie浏览器,到代表现代先进的chrome浏览器,浏览器的整个架构发生了翻天覆地的变化。本章我就来讲一下浏览器内核进程和架构的变化。 

二、进程、线程的概念

在讲之前,先来看一下会提到的两个概念,进程和线程。我们知道,ie浏览器其实是单进程的浏览器。而现代浏览器如谷歌浏览器为例,它是多进程的浏览器。那对比单进程,现代浏览器的多进程有啥优势呢,在讲这个问题之前,我们先来了解一下进程和线程的概念。

首先进程是一个程序的运行实例,我们写的代码最后运行其实就是一个进程在运行,所以进程也可以说是程序基本运行的单位。进程也有分主进程、父进程和子进程。主进程是进程的入口,如果你写过Java的话,就会了解到Java里程序执行有一个main方法,这个main方法是程序的入口。一般来说,如果写的进程够简单的话,主进程就是父进程。父子进程的关系其实就子进程得到的是除了代码段是与父进程共享的意外,其他所有的都是得到父进程的一个副本,子进程的所有资源都继承父进程,得到父进程资源的副本,既然为副本,也就是说,二者并不共享地址空间。,两个是单独的进程,继承了以后二者就没有什么关联了,子进程单独运行。

线程是独立调度和分派的基本单位,也是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在进程中,如果有多个线程处理一个代码块,我们可以称之为并行处理。比如下面这个代码块在单线程中则是要执行四步,而在多线程中前三行通过三个线程来执行,最后一行代码则在最后的线程中执行展示即可:

var a = 1 + 1
var b = 2 * 2
var c = 3 / 3
console.log(a, b, c)

总的来说,进程和线程之间的关系是这样的:

  1. 在进程里任意一个线程出错都会导致整个进程崩溃。
  2. 进程里的线程可以共享进程里的数据。
  3. 当一个进程关闭后,系统会回收进程所占用的资源。
  4. 进程与进程之间的内容相互隔离,如果要实现进程与进程之间的通信,则用IPC技术。

三、浏览器的单进程时代

在前端处于刀耕火种的年代,微软通过在它的电脑设备上绑定了ie浏览器,逐渐的打败了昔日强敌网景,独霸整个pc时代。以ie6为例,这款浏览器就是典型的单进程浏览器,而且还是单线程浏览器。所以你可以想象,这个浏览器是奇慢无比,一个网页挂了就会导致浏览器里所有的页面都挂了。于是乎在后面的ie浏览器,通过多线程技术来试图提升浏览器的速度,但其实单进程无论怎么魔改,都会涉及到这些问题:

  1. 不稳定。毕竟只要一个线程挂了就整个浏览器挂了,所以整个浏览器很不稳定。
  2. 不流畅。比如在通过网络获取资源,到渲染流程,如果浏览器上还有插件则还要考虑插件的运行,如果有一个脚本是死循环的,则导致所有页面都要等待这个脚本执行完毕,会导致整个页面十分的不流畅。并且单进程浏览器关闭一个页面也会存在内存泄漏的问题,毕竟不是多进程。多进程关掉一个页面相当于关掉一个进程,系统会自动回收资源。所以这样子会导致浏览器开的越久就越占用内存。
  3. 不安全。上面说到线程是可以共享进程里的资源的,如果一个插件是恶意的,它可以影响到整个浏览器,甚至可以控制你的电脑,来盗取账号密码。除了插件外,还有一些脚本可以通过钻漏洞来获取权限,引发很多严重的后果。

四、浏览器的多进程时代

既然单进程时代的浏览器是有这么多的漏洞,那么多进程时代的浏览器是怎么样的呢?现代浏览器以chrome浏览器为例,目前的浏览器的多进程架构大致上是这样的:

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

 正是通过多进程,很好的解决了上面所提到的三个问题。首先chrome里新开一个页面相当于新开一个渲染进程,该页面是不会影响到别的页面执行。

然后不流畅的问题,由于通过多进程,JavaScript的脚本只有在渲染进程才会被执行,所以如果出现死循环的问题也只是影响当前的页面。而内存泄漏的问题也更容易解决,因为关闭了进程系统就会自动回收资源。

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

不过笔者也有时偶然发现在chrome中,一个页面的崩溃也会导致所有页面的崩溃。原因是这样的,通常情况下是一个页面使用一个进程,但是,有一种情况,叫"同一站点(same-site)",具体地讲,我们将“同一站点”定义为根域名,还包含了该根域名下的所有子域名和不同的端口。Chrome的默认策略是,每个标签对应一个渲染进程。但是如果从一个页面打开了新页面,而新页面和当前页面属于同一站点时,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫process-per-site-instance。直白的讲,就是如果几个页面符合同一站点,那么他们将被分配到一个渲染进程里面去。所以,这种情况下,一个页面崩溃了,会导致同一站点的页面同时崩溃,因为他们使用了同一个渲染进程。

为什么要让他们跑在一个进程里面呢?因为在一个渲染进程里面,他们就会共享JS的执行环境,也就是说A页面可以直接在B页面中执行脚本。因为是同一家的站点,所以是有这个需求的。

不过多进程浏览器也有它的缺点,第一就是更高的资源占用。浏览器相当于父进程,浏览器下面的进程相当于子进程。因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。

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

五、面向服务的架构

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

Chrome 最终要把 UI、数据库、文件、设备、网络等模块重构为基础服务,类似操作系统底层服务。目前 Chrome 正处在老的架构向服务化架构过渡阶段,这将是一个漫长的迭代过程。Chrome 正在逐步构建 Chrome 基础服务(Chrome Foundation Service),如果你认为 Chrome 是“便携式操作系统”,那么 Chrome 基础服务便可以被视为该操作系统的“基础”系统服务层。同时 Chrome 还提供灵活的弹性架构,在强大性能设备上会以多进程的方式运行基础服务,但是如果在资源受限的设备上,Chrome 会将很多服务整合到一个进程中,从而节省内存占用。 

本文标签: 浏览器工作原理进程