浏览器的构成和渲染

158 阅读10分钟

进程和线程的关系

  1. 进程中给的任一线程执行出错,都会导致整个进程的崩溃
  2. 线程之间共享进程中的数据。
  3. 当一个进程关闭之后,操作系统回收进程所占用的内存。
  4. 进程之间的内容相互隔离:如果进程之间需要通信,就需要使用用于进程间通信(IPC)的机制

当打开

早期多线程架构

目前浏览器进程

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

面向未来的进程模型

TCP协议

IP: 把数据包送达目的主机

  • IP负责把数据包送达目的地
  • UDP负责把数据包送达具体应用
  • 而TCP保证了数据完整地传输,它的连接可以分为三个阶段:建立连接,传输数据和断开连接

为什么很多站点第二次打开速度快 DNS缓存和页面资源缓存两个数据缓存起来

HTTP示意图

从URL输入到页面展示

  • 浏览器进程主要负责用户交互、子进程管理和文件存储等功能
  • 网络进程是面向渲染进程和浏览器进程等提供网络下载功能
  • 渲染进程的主要职责是吧从网络下载的HTML、JavaScript、CSS、图片等资源解析为可以显示和交互的页面。因为渲染进程所有的内容都是通过网络获取的,会存在一些恶意代码对系统进行攻击,所以运行在渲染程序里的代码是不被信任的,所以chrome会让渲染进程运行在安全沙箱里,保护系统安全

从输入URL到页面展示

用户输入

1、地址栏自动判断输入的关键字是搜索内容还是请求的URL

  • 如果是搜索内容,浏览器会使用默认搜索引擎来合成带搜索关键字的URL
  • 如果判断输入内容符合URL规则,那么地址栏会根据规则,把这段内容加上协议,合成完成的URL

URL请求过程

首先,网络进程会查找本地缓存是否缓存了该资源。如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。这请求前的第一步是要进行 DNS 解析,以获取请求域名的服务器 IP 地址。如果请求协议是 HTTPS,那么还需要建立 TLS 连接。

接下来就是利用 IP 地址和服务器建立 TCP 连接。连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。

服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应头的内容了。(为了方便讲述,下面我将服务器返回的响应头和响应行统称为响应头。)

1、重定向

在接收到服务器返回的响应头后,网络进程开始解析响应头,如果发现返回的状态码是 301 或者 302,那么说明服务器需要浏览器重定向到其他 URL。这时网络进程会从响应头的 Location 字段里面读取重定向的地址,然后再发起新的 HTTP 或者 HTTPS 请求,一切又重头开始了

2、响应数据类型处理

在处理了跳转信息之后,我们继续导航流程的分析。URL 请求的数据类型,有时候是一个下载类型,有时候是正常的 HTML 页面,那么浏览器是如何区分它们呢?

答案是 Content-Type。Content-Type 是 HTTP 头中一个非常重要的字段, 它告诉浏览器服务器返回的响应体数据是什么类型,然后浏览器会根据 Content-Type 的值来决定如何显示响应体的内容。

准备渲染进程

默认情况下,Chrome 会为每个页面分配一个渲染进程,也就是说,每打开一个新页面就会配套创建一个新的渲染进程。但是,也有一些例外,在某些情况下,浏览器会让多个页面直接运行在同一个渲染进程中。

提交文档

“提交文档”的消息是由浏览器进程发出的,渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道”。

等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程。

浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面

渲染阶段

一旦文档被提交,渲染进程便开始页面解析和子资源加载了,关于这个阶段的完整过程,我会在下一篇文章中来专门介绍。这里你只需要先了解一旦页面生成完成,渲染进程会发送一个消息给浏览器进程,浏览器接收到消息后,会停止标签图标上的加载动画。

1,用户输入url并回车
2,浏览器进程检查url,组装协议,构成完整的url
3,浏览器进程通过进程间通信(IPC)把url请求发送给网络进程
4,网络进程接收到url请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程
5,如果没有,网络进程向web服务器发起http请求(网络请求),请求流程如下:
    5.1 进行DNS解析,获取服务器ip地址,端口(端口是通过dns解析获取的吗?这里有个疑问)
    5.2 利用ip地址和服务器建立tcp连接
    5.3 构建请求头信息
    5.4 发送请求头信息
    5.5 服务器响应后,网络进程接收响应头和响应信息,并解析响应内容
6,网络进程解析响应流程;
    6.1 检查状态码,如果是301/302,则需要重定向,从Location自动中读取地址,重新进行第4步
        (301/302跳转也会读取本地缓存吗?这里有个疑问),如果是200,则继续处理请求。
    6.2 200响应处理:
        检查响应类型Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行
        后续的渲染,如果是html则通知浏览器进程准备渲染进程准备进行渲染。
7,准备渲染进程
    7.1 浏览器进程检查当前url是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程
8. 传输数据、更新状态
    8.1 渲染进程准备好后,浏览器向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”
    8.2 渲染进程接收完数据后,向浏览器发送“确认提交”
    8.3 浏览器进程接收到确认消息后更新浏览器界面状态:安全、地址栏url、前进后退的历史状态、更新web页面。

浏览器不能直接理解HTML数据,所以第一步需要将其转换为浏览器能够理解的 DOM 树结构; 生成 DOM 树后,还需要根据 CSS 样式表,来计算出 DOM 树所有节点的样式; 最后计算 DOM 元素的布局信息,使其都保存在布局树中。

  • 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
  • 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
  • 创建布局树,并计算元素的布局信息。
  • 对布局树进行分层,并生成分层树。
  • 为每个图层生成绘制列表,并将其提交到合成线程。
  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  • 合成线程发送绘制图块命令DrawQuad给浏览器进程。
  • 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

1. 更新了元素的几何属性(重排)

从上图可以看出,如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

2. 更新元素的绘制属性(重绘)

从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

3. 直接合成阶段

在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。