[TOC]

前言

读完本文希望你能了解:

  • 浏览器的架构、线程、进程
  • 浏览器一次导航背后发生了什么
  • 概念的关联:浏览器内核、JS引擎、线程、进程
  • V8是如何工作的

    浏览器架构

    硬件、操作系统、软件的分层架构 浏览器是如何划分模块的,并且分配进程或线程给这些模块运行

CPU、GPU、操作系统、应用的关系

CPU 即中央处理器,可以处理几乎所有计算。以前的 CPU 是单核的,现在大部分笔记电脑都是多核的,专业服务器甚至有高达 100 多核的。CPU 计算能力很强,但只能一件件事处理。
GPU 一开始是为图像处理设计的,即主要处理像素点,所以拥有大量并行的处理简单事物的能力,非常适合用来做矩阵运算,而矩阵运算又是计算机图形学的基础,所以大量用在可视化领域。
CPU、GPU 都是计算机硬件,这些硬件各自都提供了一些接口供汇编语言调用;而操作系统则基于它们之上用 C 语言(或其它)将硬件管理了起来,包括进程调度、内存分配、用户内核态切换等等;运行在操作系统之上的则是应用程序了,所以应用程序不直接和硬件打交道,而是通过操作系统间接操作硬件。
jiagou.png

为什么应用程序不能直接操作硬件呢? 这样做有巨大的安全隐患,因为硬件是没有任何抽象与安全措施的,这意味着理论上一个网页可以通过 js 程序,在你打开网页时直接访问你的任意内存地址,读取你的聊天记录,甚至读取历史输入的银行卡密码进行转账操作。

在进程和线程上执行程序

为了让程序运行的更安全,操作系统创造了进程与线程的概念,进程可以分配独立的内存空间,进程内可以创建多个线程进行工作,这些线程共享内存空间。
因为线程间共享内存空间,因此不需通信就能交流,但内存地址相互隔离的进程间也有通信需求,需通过 IPC(Inter Process Communication)进行通信。
进程之间相互独立,即一个进程挂了不会影响到其它进程,还可以一个进程中可以创建一个新进程,并与之通信,所以浏览器就采用了这种策略,将 UI、网络、渲染、插件、存储等模块进程独立,并且任意挂掉后都可以被重新唤起。
get.svg1.svg

浏览器有哪些进程

下表展示每个 Chrome 进程与各自控制的内容:

进程 控制
Browser进程 负责浏览器页面显示,与用户交互,如前进后退等。负责各个页面的管理,创建和销毁其它进程。
Renderer进程 控制标签页内网站展示。默认每个Tab一个进程,内部是多线程的。
Plugin
进程
控制站点使用的任意插件,如 Flash。
GPU
进程
处理独立于其它进程的 GPU 任务。GPU 被分成不同进程,因为 GPU 处理来自多个不同应用的请求并绘制在相同表面。

mianProcess.png
图 9:不同进程指向浏览器 UI 的不同部分
还有更多进程如扩展进程与应用进程。如果你想要了解有多少进程运行在你的 Chrome 浏览器中,可以点击右上角的选项菜单图标,选择更多工具,然后选择任务管理器。然后会打开一个窗口,其中列出了当前正在运行的进程以及它们当前的 CPU/内存使用量。

浏览器架构

那么浏览器是怎么使用进程和线程来工作的呢?
其实大概可以分为两种架构,一种是单进程架构,也就是只启动一个进程,这个进程里面有多个线程工作。
第二种是多进程架构,浏览器会启动多个进程,每个进程里面有多个线程,不同进程通过IPC进行通信。
jiagoux.png
以chrome举例:
chrome.png
Chrome浏览器会有一个浏览器进程(browser process),这个进程会和其他进程一起协作来实现浏览器的功能。对于渲染进程(renderer process),Chrome会尽可能为每一个tab甚至是页面里面的每一个iframe都分配一个单独的进程。

Chrome 多进程架构的优缺点

优点:

  • 避免单个page crash影响整个浏览器
  • 避免第三方插件crash影响整个浏览器
  • 多进程充分利用多核优势
  • 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性

缺点:占内存
由于进程有自己的私有内存空间,所以它们通常包含公共基础设施的拷贝(如 V8)。这意味着使用了更多的内存,如果它们不是同一进程中的线程,就无法共享这些拷贝。
为了节省内存,Chrome 对可启动的进程数量有所限制。具体限制数值依设备可提供的内存与 CPU 能力而定,但是当 Chrome 运行时达到限制时,会开始在同一站点的不同标签页上运行同一进程。

节省更多内存 —— Chrome 中的服务化

Chrome 正在经历架构变革,它转变为将浏览器程序的每一模块作为一个服务来运行,从而可以轻松实现进程的拆解或聚合。
浏览器有许多独立的模块(进程和线程),比如:

  • 浏览器模块(Browser):负责整个浏览器内行为协调,调用各个模块。
  • 网络模块(Network):负责网络 I/O。
  • 存储模块(Storage):负责本地 I/O。
  • 用户界面模块(UI):负责浏览器提供给用户的界面模块。
  • GPU 模块:负责绘图。
  • 渲染模块(Renderer):负责渲染网页。
  • 设备模块(Device):负责与各种本地设备交互。
  • 插件模块(Plugin):负责处理各类浏览器插件。

通常观点是当 Chrome 运行在强力硬件上时,它会将每个服务分解到不同进程中,从而提升稳定性,但是如果 Chrome 运行在资源有限的设备上时,它会将服务聚合到一个进程中从而节省了内存占用。在这一架构变革实现前,类似的整合进程以减少内存使用的方法已经在 Android 类平台上使用。
jieou.svg

特别嘉宾:每个 iframe 的渲染进程 —— 站点隔离

站点隔离 将同一个 tab 内不同 iframe 包裹在不同的进程内运行,以确保 iframe 间资源的独占性,以及安全性。
同源策略 是 web 的核心安全模型。同源策略确保站点在未得到其它站点许可的情况下不能获取其数据。安全攻击的一个主要目标就是绕过同源策略。进程隔离是分离站点的最高效的手段。
iframe.webp

导航时发生了什么

上个小节我们研究了不同的进程和线程如何处理浏览器的不同部分。
这个小节研究每个进程和线程如何通信以显示网站。

第 1 步:处理输入内容

当用户开始在地址栏键入时,UI 线程要问的第一件事是 “这是一次搜索查询还是一个 URL 地址?”。在 Chrome 中,地址栏同时也是一个搜索输入栏,所以 UI 线程需要解析和决定把你的请求发送到搜索引擎,或是你要请求的网站。
2-1.webp

第 2 步:开始获取网页内容

当用户按下 Enter 键时,UI 线程启用network 线程获取网页内容。network thread 会寻找合适的协议处理网络请求,一般会通过 DNS 协议 寻址,通过 TLS 协议 建立安全链接。如果服务器返回了比如 301 重定向信息,network thread 会通知 UI thread 这个信息,再启动一遍第二步。
2-2.webp

第 3 步:读取响应

2-3.webp
一旦开始收到响应主体(payload),network thread 会读取响应内容。在这一步 network thread 会首先读取首部一些字节,即我们常说的响应头,其中包含 Content-Type 告知返回内容是什么。如果返回内容是 HTML,则 network thread 会将数据传送给 renderer process,但是如果是一个压缩文件或是其他文件,那么意味着它是一个下载请求,因此需要将数据传递给下载管理器。
2-4.webp
此时也会进行 SafeBrowsing 检查。如果域名和响应数据似乎匹配到一个已知的恶意网站,那么网络线程会显示一个警告页面。除此之外,还会发生 CrossOriginReadBlocking(CORB)检查,以确保敏感的跨域数据不被传给渲染进程。

第 4 步:查找渲染进程

一旦所有检查都完成,network thread 会通知 UI thread 已经准备好跳转了(注意此时并没有加载完所有数据,第三步只是检查了首字节),UI thread 会通知 renderer process 进行渲染。
2-5.webp
由于网络请求会花费几百毫秒才获取回响应,因此可以应用一个优化措施。为了提升性能,UI thread 在通知 network thread 的同时就会实例化一个 renderer process 等着,一旦 network thread 完毕后就可以立即进入渲染阶段,如果检查失败则丢弃提前实例化的 renderer process。

第 5 步:提交导航

现在渲染进程已经就绪,browser process 通过 IPC 向 renderer process 传递数据流。当browser process收到渲染进程已经就绪的消息时,导航完毕并且文档加载解析开始。
此时导航会被确认,浏览器的各个状态(比如导航状态、前进后退历史)将会被修改,同时为了方便 tab 关闭后快速恢复,会话记录会被存储在硬盘。
2-7.webp

额外的步骤:初始加载完毕

一旦导航被提交,渲染进程开始加载资源和渲染页面。当 renderer process 加载完成后(具体做了什么下一篇会说明),会通知 browser process onLoad 事件,此时浏览器完成最终加载完毕状态,loading 圆圈也会消失,各类 onLoad 的回调触发。
之所以说“结束”,是因为客户端 JavaScript 可以在这时之后仍然加载额外的资源并且渲染新视图(初始加载结束)。
2-8.webp

渲染进程处理网站内容

这小节宏观的介绍 renderer process 做了哪些事情。
浏览器 tab 内 html、css、javascript 内容基本上都由 renderer process 的主线程处理,除了一些 js 代码会放在 web worker 或 service worker 内,所以浏览器主线程核心工作就是解析 web 三剑客并生成可交互的用户界面。
3-1.webp

1、解析(Parsing)- 构建DOM

DOM 的构建

当渲染进程收到导航的提交消息并开始接收 HTML 数据时,主线程(实际上是GUI渲染线程)开始解析文本字符串(HTML)并将其转换为文档对象模型(DOM)。将 HTML 到 DOM 的解析由 HTML Standard 规定。

tip1:子资源加载

网站通常使用图像、CSS 和 JavaScript 等外部资源,这些文件需要从网络或缓存加载。在解析构建 DOM 时,主线程按处理顺序逐个请求它们,但为了加快速度,“预加载扫描器(preload scanner)”会同时运行。如果 HTML 文档中有 了解现代浏览器 - 图17 之类的内容,则预加载扫描器会查看由 HTML 解析器生成的标记,并在浏览器进程中向网络线程发送请求。
3-2.webp

tip2:JavaScript 阻塞解析

当 HTML 解析器遇到