浏览器中的进程和线程

1,096 阅读7分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

简介

  • 进程是CPU资源分配的最小单位。是能拥有资源和独立运行的最小单位,不同进程之间不会共享资源。
  • 线程是CPU调度的最小单位。一个进程中可以有多个线程,多个线程之间共享进程的资源。
  • 不同进程之间也能通信,但是代价较大。

用公司和员工来做比喻,进程代表公司,线程代表员工:

  • 不同公司(进程)之间,不会共享资产/资源(办公用品、内部系统、客户信息等)
  • 一个公司中可以有多个员工,员工是没有自己的资源和空间的,但是他们可以使用和共享公司的资源和空间
  • 每个公司(进程)之间可以交流(通信),但是代价较大

浏览器中的进程

  • 浏览器是多进程
  • 浏览器每多开一个 tab,都相当于系统为其创建了一个独立的进程
  • 浏览器之所以能够运行,是因为系统为其进程分配了资源

进程之间是相互独立的,线程是资源共享的:

有时我们会发现浏览器的某个 tab 挂了,但是其他 tab 可以正常运行,这就很好的说明了每个 tab 都是一个独立的进程。因为如果 tab 是线程的话,一个线程挂了,其他线程也会受到影响。

1、浏览器(Browser)进程

浏览器的主进程(只有一个),负责协调、主控。

  • 负责浏览器的界面显示,用户交互,网址输入、前进、后退等。
  • 负责各个页面的创建和销毁。
  • 将页面内容写入到浏览器内存中,最后将图像显示在屏幕上。
  • 文件存储等功能

Browser进程中的线程:

  • UI线程:该线程是程序运行的主线程,是程序的入口点,用来处理用户交互(如监听用户输入、前进后退等)。同时,会分发任务给其他相应线程去执行。
  • IO线程:处理 Browser 进程与其他进程进行进程间通信,下载 Renderer 进程所需的资源文件等。
  • File线程:读取磁盘文件,下载文件到磁盘等。
  • 数据库线程:进行一些数据库操作,例如保存 Cookie 到数据库
  • 历史记录线程
  • http服务代理线程
  • ***

2、渲染(Renderer)进程

渲染进程内部是多线程的。

默认情况下,一个 tab 配一个渲染进程。主要的作用是页面渲染、脚本执行、事件处理等。

Renderer进程中的线程:

  • GUI渲染线程
  • JavaScript引擎线程
  • 事件触发线程
  • 定时器线程
  • 异步http请求线程

3、GPU进程

用于3D绘制,将开启了3D绘制的元素渲染由CPU转向GPU,最多一个。

也就是GPU加速。

4、网络进程

主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程中,现在独立出来,成为一个单独的进程。

5、插件进程

每种类型的插件对应一个进程,仅当使用该插件是才创建。

6、音频进程

浏览器的音频管理。

Browser进程与Renderer进程间的通信

以开启一个新 tab 为例

  • Browser进程中的UI线程处理用户交互,接收到用户请求,转交给IO线程
  • Browser进程中的IO线程获取页面内容(通过网络请求或本地缓存),随后将该任务通过 RendererHost 接口传递给 Renderer 进程
  • Renderer进程Renderer接口 收到消息,简单解释后交给渲染线程,进行 htmlcss 解析,渲染页面,js 执行等任务
  • Renderer 进程将得到的结果传递给 Browser 进程
  • Browser进程 接收到结果并在界面上绘制出图像

优势

相较于单进程而言,多进程的优势:

  1. 避免单个页面奔溃影响整个浏览器
  2. 避免第三方插件奔溃影响整个浏览器
  3. 多进程可以充分利用多核优势
  4. 方便使用沙盒模型隔离插件等进程,提高浏览器的稳定性

空间换时间

浏览器的线程(这里都是Renderer进程中的线程)

页面的渲染是多线程的;JS是单线程(会阻塞)的

常用的线程有 JS引擎线程HTTP请求线程定时触发线程事件处理线程GUI渲染线程

1、JS引擎线程

  • 也称为JS内核,负责处理 JavaScript 脚本程序。(例如V8引擎)。
  • JS引擎线程负责解析 JavaScript 脚本,然后运行代码。
  • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个 Tab页(render进程)中无论什么时候都只有一个 JS线程 在运行JS程序
  • GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

2、HTTP请求线程

  • XMLHttpRequest 在连接后会通过浏览器新开一个线程来进行请求(异步)。
  • 检测到状态变更时,如果设置有回调函数,异步线程就会产生状态变更事件,将这个回调放入事件队列中。再交由 JavaScript引擎 执行。

3、定时触发器线程

setTimeoutsetInterval

  • 浏览器的定时器并不是由 JavaScript引擎来计数的。(因为 JavaScript引擎 是单线程的,如果处于阻塞线程状态的话就会影响计时器的准确性)
  • 因此通过单线程来计时并触发定时。(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)

注意:在 W3C的HTML标准中规定,要求 setTimeout 中低于 4ms 的时间间隔算作 4ms。

4、事件处理线程

事件处理线程归属于浏览器而不是JS引擎,用来控制事件循环。(JS引擎自己都忙不过来,需要浏览器另外开辟线程来协助处理)。

JS引擎执行代码时(如果其中有异步、鼠标点击事件等),会将对应任务添加到事件现场中。

当对应的事件符合条件并被触发时,事件处理线程会把事件添加到待处理队列的尾部,等待JS引擎的处理。

注意:由于JS是单线程的原因,所以这些待处理队列中的事件都是排队等待JS引擎处理(等到JS引擎空闲的时候再去执行)

5、GUI渲染线程

GUI渲染线程:负责渲染浏览器界面,解析HTML、CSS,构建DOM树和 Render 树,进行布局和绘制等。

触发执行:当页面需要重绘或由于某种操作引发回流(重排)时,GUI渲染线程就会触发。

注意:GUI渲染线程与JS引擎是互斥的,当JS引擎执行时,GUI线程会被挂起,GUI更新会被保存在一个队列中直到JS引擎空闲时才会继续执行。

总结

  • 浏览器是多进程的
  • JS执行的主线程为JS引擎,并且无论何时都只有一个JS线程在执行,所以是单线程的。
  • GUI渲染线程和JS引擎线程是互斥的,JS会阻塞页面的加载和渲染
  • 定时器(setTimeoutsetInterval)会在定时触发线程中进行计时
  • 定时触发线程计时结束后需要执行的事件和异步HTTP请求线程的回调事件都会进入到事件触发线程任务队列中等待JS引擎的执行。