浏览器的进程与线程

169 阅读7分钟

仅针对Chrome浏览器,如有错误,欢迎指正

前置知识

什么是进程?

进程是操作系统分配资源的单位,是一个程序的运行实例,可以理解成操作系统给程序分配的内存空间。每个程序应用至少有一个进程,且多个进程之间相互独立。

什么是线程?

线程是调度的基本单位。一个进程至少有一个线程,在进程开启后会自动创建一个的线程来执行代码,该线程为主线程。如果程序需要执行多块代码,主线程可以开启更多的子线程来执行,所以一个进程中是可以包含多个线程的。

线程与进程关系

  • 多线程可以并行处理任务,但是线程不能单独存在,需要由进程来启动和管理。
  • 进程间任意线程的执行错误都能会导致整个进程的崩溃
  • 线程之间共享进程中的数据
  • 关闭进程,则会回收进程所占用内存
  • 进程间的内容相互隔离

浏览器模型

进程模型

Chrome支持下面四种模式:

  1. Process-per-site-instance:每一站点实例一个进程。就是你打开一个网站,然后从这个网站链开的一系列网站都属于一个进程。这是Chrome的默认模式。 ​
  2. Process-per-site:每一站点一个进程。
  3. Process-per-tab:这个简单,一个tab一个进程
  4. SingleProcess:传统浏览器的模式:没有多进程只有多线程,用–single-process开启。

当前浏览器的进程和线程

浏览器内部工作及其复杂,当启动浏览器时它会自动启动多个进程,进程间通过IPC通信 image.png 主要进程有:

  1. 浏览器进程(Browser进程): 浏览器的主进程,负责界面显示、用户交互、子进程管理、地址栏、书签、及前进后退。将从渲染进程得到的位图绘制到用户界面

  2. GPU进程: Chrome的UI界面交由GPU进程,GPU进程在交由显卡绘制

  3. 网络进程: 负责页面的网络资源加载

  4. 存储进程: 管理本地存储

  5. 插件进程: 管理扩展程序中的插件

  6. 音频进程: 处理音频

  7. 渲染进程(Renderer进程): 进程启动后会开启一个渲染主线程,负责执行JS、HTML、CSS代码

image.png

借图

image.png 浏览器会为每个标签页开启一个新的渲染进程,以保证不同标签页不互相影响。排版引擎和V8引擎也是运行于渲染进程。出于安全考虑,渲染进程是运行在沙箱模式下的。由于插件极易崩溃,所以也需要有一个进程来隔离,保证崩溃了不会对页面造成影响。

渲染进程

渲染进程即浏览器内核,它是多线程,在内核控制下各线程相互配合以保持同步。其中常驻线程有:

  1. GUI渲染线程:负责渲染浏览器界面HTML元素,当界面需要重绘时,该线程就会执行。在JS线程运行时会被挂起。
  2. Javascript引擎线程:负责执行JS代码
  3. 定时器触发线程 当计时器计时时间达到,回调函数会被放置计时器队列,等待JS引擎执行回调
  4. 事件触发线程 当事件被触发,回调函数会被放置浏览器的交互队列队尾,等待JS引擎执行回调
  5. 异步http请求线程 新开一个线程请求,检测到状态变更时,将回调函数放到队列等待执行

JS为什么是单线程?

这是因为JS语言使命就是为处理页面中用户的交互,以及操作DOM数,CSSOM数来给用户呈现动态而丰富的交互,还有就是处理与服务器的交互。如果JS是多线程,就可以能出现两个线程同时操作同一个DOM,一个删除一个修改,这时的结果就难以决定了。参看数据库中两条sql操作,只有有且只有一个带锁的指令可以拿到操作权限,就可以避免这种问题。而为了避免引入锁导致语言更复杂,所以在最初就选择了单线程执行。

那为什么GUI渲染线程跟JS线程互斥呢

由于JS是可操作DOM的,如果在修改这些元素属性的同时渲染界面,那渲染线程前后获得的元素数据可能不一致,为了防止出现不可预期的结果,浏览器将GUI线程设置为跟JS线程互斥的关系。当JS引擎执行时,GUI现成会被挂起,GUI更新会被保存到一个队列中等待空闲的时候被执行

定时器是JS引擎来计时的吗

浏览器定时计数器不是JS引擎来计数的,因为JS是单线程,如果处于阻塞状态会影响计时,所以交给单独的定时器触发线程来执行。我们知道JS中的定时器是属于WEB API,而操作WEB API的是宿主环境,也就是浏览器或者Node。

浏览器进程和渲染进程的通信

打开浏览器的标签页时,浏览器做了以下动作:

  • 浏览器进程收到用户请求,通过网络下载获取页面内容,然后将该任务通过RenderHost接口传递给渲染进程
  • 渲染进程收到消息后,交由GUI渲染线程开始渲染
  • GUI渲染线程接到请求后,加载网页并渲染页面,这个过程中浏览器进程获取资源和GPU进程来帮助渲染
  • 渲染进程将结果(bitmap)传递给浏览器器进程
  • 浏览器进程收到结果,将结果绘制出来

image.png

浏览器内核由网络进程、浏览器、GPU进程及其他组成,渲染内核就是渲染进程。所有的网络资源通过浏览器内核下载,下载后的资源通过IPC将其交给渲染进程。渲染进程对这些资源进行解析、生成render🌲、布局、 分层、绘制等操作,最终生成位图。之后交给浏览器内核,由浏览器内核来将图片显示到界面。

这个时候就会想到一个问题,为什么渲染进程不自己来下载资源呢,又为什么渲染进程不自己来展示图片呢?

那就不得不提一下安全沙箱了

安全沙箱

安全沙箱是操作系统给进程上的一把锁,它限制了沙箱里的程序访问读写操作系统的文件。这样即使在渲染进程或者插件中执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限

因为网络资源的内容存在各种可能性,所以浏览器认为网络资源都不可信。但是紫瑶不执行这些网络资源,那么恶意程序就不会生效。所以浏览器内核可以放心下载这些资源。但是涉及到解析代码,执行代码,就可能被黑客攻击。渲染进程外层有个安全沙箱,也就是说即使渲染进程被攻击,黑客也获取不到渲染进程外的操作权限。也就是这个安全沙箱,渲染进程在需要访问系统资源时,就需要浏览器内核来实现,所以需要浏览器来下载这些资源。同理,展示界面的时候也一样。

沙箱是如何影响各个模块的功能呢?

由于安全沙箱,渲染进程无法访问用户的文件系统、网络、IO,这些都要由浏览器内核来实现,通过IPC将结果转发给渲染进程。就比如存储Cookie,浏览器内核来维护Cookie数据库。HTTP请求,浏览器内核来检查权限。抑或是用户交互,也是由浏览器来接受各种IO事件,然后转发给渲染进程。

浏览器多进程架构的问题

多进程模型虽然提升了浏览器的稳定性、流畅性和安全性,但同样也带来了一些问题。

  • 更高的资源占用。每个标签页单独的渲染进程,渲染进程中都包含了JS的运行环境(V8引擎),这意味着浏览器会消耗更多的内存资源。 -更复杂的体系架构。浏览中各模块耦合性高、扩展性差、牵一发而动全身。