漫画图解 Chrome 浏览器从输入到渲染的原理(7000 字)

共 10569字,需浏览 22分钟

 ·

2022-02-18 05:09

文章作者:道道里,已授权 

https://juejin.cn/post/7039036362653171742

封面图来自作者:Merryweather Comics

前言

Chrome comic,推荐一本Chrome架构简要概述的漫画,Chrome架构于2008年同Chrome浏览器一起发布,原文地址[1]中文翻译[2]

漫画目录如下:

  • 开源浏览器背后的故事
  • 稳定性、严格和多任务架构
  • 速度:Webkit和V8
  • 搜索和用户体验
  • 安全性、沙盒模式和没有危险的浏览
  • Gears,标准和开放源代码

一、CPU,GPU内存和多进程架构

计算机的核心是 CPU 和 GPU

CPU

CPU是计算机的大脑,可以处理许多不同的任务,大多数CPU都是单芯片。一个内核相当于同一个芯片中的另一个CPU。

GPU

GPU最初为图形处理开发,擅长处理简单的任务,同时跨多个CPU。

通常,应用程序使用操作系统提供的机制在 CPU 和 GPU上运行。

进程和线程

进程可以被描述为一个应用程序的执行程序,线程是存在于进程内部并执行其进程程序的任何部分的线程。

程序在启动的时候会创建一个进程,程序也可能会创建线程来帮助它工作。操作系统为进程提供了一块“内存块”以供使用,并且所有应用程序状态都保存在该私有内存空间中。当关闭应用程序时,该进程也会消失,操作系统会释放内存。

一个进程可以要求操作系统启动另一个进程来运行不同的任务,这时候会为新进程分配内存的不同部分。如果两个进程需要聊天,它们就需要 IPC。如果工作进程无响应,它可以重新启动而无需停止运行应用程序不同部分的其他进程。

memory.svg

浏览器架构

对于浏览器,可以使一个进程有许多不同的线程,也可以是许多不同的进程有多个线程通过 IPC 通信的。

而对于Chrome浏览器,最新架构如下图:

进程作用
Browser浏览器进程,控制应用程序的“chrome”部分,包括地址栏、书签、后退和前进按钮。还处理 Web 浏览器的不可见的特权部分,例如网络请求和文件访问。
Renderer渲染器进程,控制显示网站的选项卡内的任何内容。
Plugin插件进程,控制网站使用的任何插件,例如 flash。
GPU图形处理进程,独立于其他进程处理 GPU 任务。它被分成不同的进程,因为 GPU 处理来自多个应用程序的请求并将它们绘制在同一个表面上。

下图为不同进程指向浏览器UI的不同部分:

当然也还有更多的进程,比如扩展进程和实用程序进程等等。

Chrome中多进程的优势

假设,打开了三个选项卡,每个选项卡都由一个独立的渲染器进程运行。如果一个选项卡变得无响应,那可以关闭无响应的选项卡并继续操作,同时保持其他选项卡的活动。如果所有选项卡都在一个进程上运行,当一个选项卡变得无响应时,所有选项卡都无响应。

将浏览器的工作分成多个进程的另一个好处是安全性和沙箱。由于操作系统提供了一种方法来限制进程的权限,浏览器可以从某些功能通过沙箱来执行某些进程。例如,Chrome 浏览器限制对处理任意用户输入的进程(如渲染器进程)的任意文件访问。

因为进程有自己的私有内存空间,所以它们通常包含公共基础设施的副本(比如 V8,它是 Chrome 的 JavaScript 引擎)。这意味着更多的内存使用量,因为如果它们是同一进程内的线程,则无法像它们那样共享它们。为了节省内存,Chrome 限制了它可以启动的进程数。该限制取决于设备的内存和 CPU 能力,但当 Chrome 达到限制时,它会开始在一个进程中运行来自同一站点的多个选项卡。

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

Chrome 正在经历架构更改,以将浏览器程序的每个部分作为一项服务运行,从而可以轻松地拆分为不同的进程或聚合为一个进程。

当 Chrome 在强大的硬件上运行时,它可能会将每个服务拆分为不同的进程以提供更高的稳定性,但如果它在资源受限的设备上,Chrome 会将服务合并到一个进程中以节省内存占用。在此更改之前,已在 Android 等平台上使用了类似的方法来合并进程以减少内存使用量。

站点隔离

站点隔离为每个跨站点 iframe 运行单独的渲染器进程,并在不同站点之间共享内存空间。同源策略是网络的核心安全模型,它确保一个站点在未经同意的情况下无法访问其他站点的数据。对于攻击者来说,绕过同源策略是安全攻击的主要目标,对于浏览器而言,需要使用进程来分隔站点。自 Chrome 67 以来,桌面上默认启用站点隔离,选项卡中的每个跨站点 iframe 都有一个单独的渲染器进程, 当然,也从根本上改变了 iframe 相互通信的方式。

二、导航跳转

在浏览器中写了一个URL,然后浏览器从 Internet 获取数据并显示一个页面,对于请求站点和浏览器渲染前都做了什么?

前面我们知道,选项卡之外的所有内容都由浏览器进程处理,也就是 Browser Process。浏览器里的进程里有一些线程,比如绘制 Button 和 Input 的 UI 线程、处理网络堆栈以从 Internet 接收数据的网络线程、控制对文件访问的存储线程等等。在地址栏中输入 URL 时,输入由浏览器进程的 UI 线程处理。

开始

  1. 第一步:处理输入

    当地址栏中输入内容时,UI 线程首先询问的是“这是搜索查询还是 URL?”。在 Chrome 中,地址栏也是一个搜索输入字段,因此 UI 线程需要解析并决定是将它发送到搜索引擎,还是发送到请求的站点。

  1. 第二步:开始寻找

    按下回车键时,UI 线程会发起网络请求以获取站点内容。Loading spinner 显示在选项卡的一角,网络线程通过适当的协议,如 DNS 查找和为请求建立 TLS 连接。

    此时,网络线程可能会收到服务器重定向标头,如 HTTP 301。在这种情况下,网络线程会与服务器请求重定向的 UI 线程通信。然后,将发起另一个 URL 请求。

  2. 第三步:读取响应

    一旦响应的开始进入,也就是请求的 Payload,网络线程会在必要时查看流的前几个字节。响应的 Content-Type 标头应该说明它是什么类型的数据,但由于它可能丢失或错误, 因此在这里完成 MIME 类型校验

    如果响应是一个 HTML 文件,那么下一步是将数据传递给 GPU 进程,但如果它是一个 zip 文件或其他一些文件,那么它就是一个下载请求,接着他们需要将数据传递给下载管理器。

    也正是在这个地方进行安全浏览检查,如果域和相应数据跟恶意网站匹配,网络线程就会发出警报并显示警告页面,而 CORS检查 也发生在这个过程,为了确保敏感跨站点数据不扔给渲染器。

  3. 第四步:查找渲染器进程

    一旦完成所有检查并且网络线程确信浏览器应该导航到请求的站点,网络线程就会告诉 UI 线程数据已准备就绪。UI线程然后找到一个渲染器进程来进行网页的渲染。

    由于网络请求可能需要数百毫秒才能获得响应,因此应用了优化以加快此过程。当 UI 线程在第 2 步向网络线程发送 URL 请求时,它已经知道他们要导航到哪个站点。UI 线程尝试与网络请求并行地主动查找或启动渲染器进程。这样,如果一切按预期进行,当网络线程接收到数据时,渲染器进程已经处于待机状态。如果导航重定向跨站点,则可能不会使用此备用进程,在这种情况下,可能需要不同的进程。

  4. 第五步:提交

    现在数据和渲染器进程已经准备就绪,一个 IPC 从浏览器进程发送到渲染器进程以提交导航。它还传递数据流,因此渲染器进程可以继续接收 HTML 数据。一旦浏览器进程听到在渲染器进程中发生提交的确认,导航就完成了,文档渲染阶段开始。

    此时,地址栏已更新,安全指示器和站点设置 UI 反映了新页面的站点信息。选项卡的会话历史将更新,因此后退/前进按钮将逐步浏览刚刚导航到的站点。为了在关闭选项卡或窗口时促进选项卡/会话恢复,会话历史记录存储在磁盘上。

  5. 其他步骤

    提交后,渲染器进程会继续加载资源并渲染页面。渲染器进程“完成”渲染后,它会将 IPC 发送回浏览器进程(这是在 onload页面中的所有帧上触发所有事件并完成执行之后)。此时,UI 线程停止选项卡上的 加载小loading。

    在此之后客户端 JavaScript 仍然可以加载额外的资源并呈现新的视图。

导航到其他站点

如果用户再次将不同的 URL 放入地址栏会发生什么?浏览器进程通过相同的步骤导航到不同的站点。但在此之前,它需要检查当前呈现的站点是否有 beforeunload 事件。

beforeunload 可以创建一个 “离开此站点?” 的事件, 当离开或关闭选项卡时发出警报。选项卡内的所有内容(包括 JavaScript 代码)都由渲染器进程处理,因此当新的导航请求传入时,浏览器进程必须检查当前的渲染器进程。

注意:不要添加无条件beforeunload处理程序。它会产生更多的延迟,因为需要在导航开始之前执行处理程序。仅在需要时才应添加此事件处理程序,例如,如果需要警告用户他们可能会丢失在页面上输入的数据。

当新导航到达与当前呈现的站点不同的站点时,将调用一个单独的呈现进程来处理新的导航,同时保留当前的呈现进程以处理诸如 unload。有关页面生命周期状态,可以看 这里[3]

下图为从浏览器进程到新渲染器进程的 2 个 IPC,告诉渲染页面并告诉旧渲染器进程卸载:

Service Worker

首先,Service Worker 允许开发者更好地控制本地缓存的内容以及何时从网络获取新数据。如果 service worker 设置为从缓存加载页面,则无需从网络请求数据。

注意:Service Worker 是在渲染器进程中运行的 JavaScript 代码

但是当导航请求进来时,浏览器进程又如何知道哪个站点有Service Worker?

注册Service Worker后,Service Worker的作用域将会保留。当导航发生时,网络线程会根据注册的 Service Worker 范围检查域,如果 Service Worker 已为该 URL 注册,则 UI 线程会查找渲染器进程以执行 Service Worker 代码。Service Worker 可能会从缓存中加载数据,从而无需从网络请求数据,或者它可能会从网络请求新资源。

下图为浏览器进程中的 UI 线程启动渲染器进程来处理服务工作者;渲染器进程中的工作线程然后从网络请求数据:

导航预加载

如果 Service Worker 最终决定从网络请求数据,浏览器进程和渲染器进程之间的这种往返可能会导致延迟。Navigation Preloads 是一种通过在 Service Worker 启动的同时加载资源来加速此过程的机制。它用标头标记这些请求,允许服务器决定为这些请求发送不同的内容;例如,只是更新数据而不是完整文档。

三、渲染

导航过后,浏览器会调用渲染器(UI)进程工作。

渲染器进程处理Web

渲染器进程负责选项卡内发生的所有事情。在渲染器进程中,主线程处理发送给用户的大部分代码。如果使用 Web Worker 或 Service Worker,有部分 JavaScript 由工作线程处理。合成器和光栅线程也在渲染器进程内运行,以高效、流畅地渲染页面。

渲染器进程的核心工作是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页。

解析

构建DOM

当渲染过程接收提交消息用于导航和开始接收HTML数据,主线程开始解析HTML,使之成为一个 DOM

DOM 是浏览器对页面的内部表示,也是开发人员可以通过 JavaScript 与之交互的数据结构和 API。将HTML文档解析为DOM是由HTML标准定义的,所以有时候写错标签,也会被自动纠正,具体可以查看 解析器中的错误处理[4]

子资源加载

对于图像、CSS 和 JavaScript 等外部资源,需要从网络或缓存加载。主线程可以在解析构建DOM的过程中找到它们后一一请求,但为了加快速度,“预加载扫描器(preload scanner)” 是并发运行的。如果HTML 文档中有类似或 ,预加载扫描器会查看 HTML 解析器生成的 token,并将请求发送到浏览器进程中的网络线程。

JavaScript 可以阻止解析

当 HTML 解析器找到一个