js异步——浅谈Chrome浏览器架构

发布时间 2023-04-09 14:32:27作者: 菲尼克斯交警

前言

在讲述事件循环和消息队列之前,需要了解 JS 的单线程执行机制,JS 的执行是从上到下依次执行的,这些便是同步任务,而异步操作类似于系统中断,即当前进程外部的实体(主线程之外的、宿主环境提供的、特殊的线程,如IO线程(HTTP请求)和定时器线程等)可以触发代码执行,然后在异步任务完毕后,执行回调。

异步任务不同于顺序执行的同步任务,异步任务的回调函数对于 JS 运行时来说是一个黑盒,无法预知他究竟什么时候会被执行,因为这取决于异步回调何时从消息队列中出队执行,而消息队列中的异步回调是否出队,则与事件循环机制直接相关。

从多进程(process)和单线程(thread)谈起

人们使用的现代浏览器都是多进程的应用程序,而运行在浏览器上的 JS 代码是单线程的。

浅谈Chrome浏览器架构

​ 如果自己设计一个浏览器,浏览器可以是哪种架构呢?

  • 单进程架构(线程间进行通信)
  • 多进程架构(进程间 IPC (Inter-Process Communication)通信)

如果你的浏览器要以单进程架构进行设计,需要在一个进程内实现网络、调度、存储、IO设备、渲染、插件等任务,当然你可以把这些任务分为若干个线程去执行,形成单进程多线程的浏览器架构。

但是由于这些任务在现在操作系统中越来越复杂,例如把网络、存储、渲染这些任务放在一个线程中,执行效率和性能越来越低下,且无法再向下拆分出类似线程的子空间,因为线程已经是最小的执行单位。

因此,为了强化浏览器的各个复杂功能,出现了多进程架构的浏览器,可以将网络、存储、渲染、IO、插件这些复杂任务分配给一个个单独的进程,这样每个进程又能向下拆分出多个线程,极大程度上强化了浏览器。

理解Chrome的多进程架构

Chrome也是基于多进程架构的现代浏览器,Chrome的主要进程组成如下:

  • Browser 进程:Tab之外的一切都有该进程处理。负责地址栏、书签栏、前进后退、网络请求、文件访问等;

  • Renderer 进程:负责一个 Tab 内所有和网页渲染有关的事情,是最核心的进程;

  • Plugin 进程:负责 Chrome 插件相关的任务;

  • GPU 进程:GPU进程与其他浏览器进程相隔离处理GPU任务,比如把浏览器绘制到屏幕上;

    所有应用程序都要在OS的调度下基于CPU和GPU的计算才能运行。因为GPU要处理多个应用程序的的请求,浏览器的的GPU进程只是一个分量。GPU擅长处理图形,因此提供GPU计算的应用程序可以实现快速渲染和平滑交互。

Chrome 的每一个Tab 选项卡都拥有自己的 Renderer 进程,有三个 Tab 就意味着有三个不同的 Renderer 进程这样可以保证多个 Tab 之间互不影响,即使其中一个 Tab 没有响应,也不影响其他 Tab 的正常执行。然而,由于进程是 OS 中拥有资源的独立单位,多个 Tab 之间的数据是非共享的,这也意味着多个 Tab 都会有相同的 V8引擎初始化数据,这意味着更多的内存使用。

了解 Browser 浏览器进程

简单来说,在浏览器中,Tab之外的一切都归浏览器进程所接管,它包含3个主要的线程:

  • UI thread UI线程:负责绘制和管理浏览器的按钮和输入框区域。
  • Network thread 网络线程:负责处理网络堆栈以从互联网接收数据
  • Storage thread 存储线程:负责控制文件访问

现在让我们来模拟一个在地址栏输入网址,并将网页呈现在浏览器上的过程

  1. 用户在地址栏中键入字符串,UI 线程会识别该字符串是 URL 还是搜索关键词。

    Chrome中的地址既可以访问网页,同时又是个搜索框,这里假设我们输入的是 URL。

  2. UI 线程通知网络线程开始进行导航,发起网络请求

  3. 读取响应数据,如果响应的是 HTML 文件,那么下一步会将该数据传递给渲染进程;但如果响应数据是一个压缩包或其它类型的文件,那么就意味着我们发送的是下载请求,所以需要把数据传递给下载管理器

  4. UI线程负责找到渲染进程,通知它要进行网页渲染

  5. 此时数据和渲染进程都已经准备好,浏览器进程和渲染进程开启 IPC 传递数据,导航部分完成,开始进行网页渲染。

了解最为重要的 Renderer 渲染进程

渲染进程主要包括4个线程:

  • Main thread 主线程:执行js、下载资源、计算样式、进行布局、绘制合成
  • Raster thread 光栅线程
  • Compositor thread 合成线程
  • Worker thread 工作者线程
主线程的功能
  • 下载资源:主线程可以通过 Browser 线程的 Network 线程下载图片、css、js等渲染DOM需要的资源文件

  • 执行 JS:主线程在遇到 <script> 标签时会阻塞HTML文档的解析,并必须先下载、解析和执行js代码,why?因为 js 可以用 document.write()之类的东西改变 DOM 结构。这就是为什么会暂停HTML的解析,并等待js代码执行完毕后才能恢复。

  • 计算样式:主线程会基于CSS选择器或者浏览器默认样式去进行样式计算,最终生成 computed style

  • 进行渲染:主线程根据先后顺序以及层级关系对 DOM 元素进行渲染,通常会生成多个图层

  • 最终合成:主线程将渲染后的多个 frame 帧合成,类似flash的帧动画和PS的图层

关于合成线程
  • 浏览器滚动时,合成线程会创建一个新的合成帧发送给 GPU
  • 合成线程工作与主线程无关,不同等待样式计算和 js 的执行,因此合成线程相关的动画比涉及到主线程重新计算样式和执行 js 的动画更加流畅
主线程、合成线程和光栅线程配合工作的过程
  1. 主线程主要遍历布局树,生成层树

  1. 栅格线程栅格化磁铁到 GPU

  1. 合成线程将此贴合成帧并通过 IPC 传递给 Browser进程,显示在屏幕上。

参考

Inside look at modern web browser (part 1)

浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务

Inside look at modern web browser (part 2)

Inside look at modern web browser (part 3)