浏览器的执行(1)-进程和线程

1,353 阅读6分钟

区分进程和线程

我们先回顾下官方的术语:

进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

为了便于理解,我们看一个详细点的比喻:

  • 进程是一个工厂,工厂有它的独立资源
  • 工厂之间相互独立
  • 线程是工厂中的工人,多个工人协作完成任务
  • 工厂内有一个或多个工人
  • 工人之间共享空间

再完善完善概念:

  • 工厂的资源:系统分配的内存(独立的一块内存)
  • 工厂之间相互独立:进程之间相互独立
  • 多个工人协作完成任务:多个线程在进程中协作完成任务
  • 工厂内有一个或多个工人:一个进程由一个或多次线程构成
  • 工人之间共享空间:同一个进程下,各个线程共享程序之间的内存空间(包括代码段、数据集、堆等)

注意:

  • 不同进程之间也可以通信,不过代价较大
  • 现在,一般通用的叫法:单线程与多线程,都是指在一个进程内的单和多。(所以核心还是得属于一个进程才行)

浏览器是多进程的

浏览器是多进程的。简单理解,每打开一个tab,就相当于创建了一个新的浏览器进程。

在windows电脑中,打开多个Chrome浏览器页面,在任务管理器-进程中,可以看见Chrome下有多个进程显示(分别是每个Tab页面的独立进程,以及一个主进程)。

浏览器都包含哪些进程?

在了解浏览器是多进程后,我们了解下浏览器包含的主要进程:

  1. Browser进程 浏览器的主进程,仅一个:
    • 负责浏览器界面显示,与用户交互。如前进,后退等
    • 负责各个页面的管理,创建和销毁其他进程
    • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
    • 网络资源的管理,下载等
  2. 第三方插件进程
    • 每种类型的插件对应一个进程,仅当使用该插件时才创建
  3. GPU进程,仅一个
    • 用于3D绘制等。
  4. 浏览器渲染进程(浏览器内核)(Renderer进程),默认每个tab一个,互不影响。作用于:
    • 页面渲染,脚本执行,事件处理等

当然,浏览器有时会将多个进程合并(譬如打开多个空白标签页后,会发现多个空白标签页被合并成了一个进程)。

浏览器多进程的优势

相比于单进程的浏览器,使用多进程有很多优点:

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

渲染进程(Renderer进程)

在这么多进程当中,前端接触最多,也最重要的进程是渲染进程

这个进程是多线程的,会执行页面的渲染、js的执行、事件的循环等操作。

那么,我们看看都有哪些线程来实现这些操作:

  1. GUI渲染线程
    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    • GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
  2. JS引擎线程
    • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
    • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序(单线程)
    • 同理,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
  3. 事件触发线程
    • 归属于浏览器,用于控制事件循环
    • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
    • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,排队等待JS引擎线程的处理(当JS引擎线程空闲时会进行处理)
  4. 定时触发器线程
    • 传说中的setInterval与setTimeout所在线程
    • 浏览器定时计数器并不是由JavaScript引擎计数的,而是通过单独线程来计时并触发定时。计时完毕后,添加到事件队列中,等待处理。
    • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
  5. 异步http请求线程
    • 在XMLHttpRequest在连接后,是通过浏览器新开一个线程请求
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,等待处理。

Browser进程和渲染进程的通信过程

接下来,我们了解下,浏览器的主进程(Browser进程)是如何与渲染进程产生通信的。

上面我们了解到,当我们打开一个tab时,任务管理器中出现了两个进程(一个是主控进程,一个则是打开Tab页的渲染进程)。这里,我们将这个过程进行简化:

  • Browser进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给渲染进程
  • 渲染进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染。具体渲染流程可以查看这里
    • 这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
  • 渲染完毕后,渲染进程将结果传递给Browser进程,Browser进程接收到结果并将结果绘制出来。

简单绘制下上面的流程:

参考