多进程架构
进程和线程
什么是 并行处理
?同一时刻处理多个任务。
比如我们要计算下面这三个表达式的值,并显示出结果。
A = 1+2
B = 20/5
C = 7*8
在编写代码的时候,我们可以把这个过程拆分为四个任务:
任务 1 是计算 A=1+2; 任务 2 是计算 B=20/5; 任务 3 是计算 C=7*8; 任务 4 是显示最后计算的结果。
正常情况下程序可以使用单线程来处理,也就是分四步按照顺序分别执行这四个任务。
如果采用多线程,会怎么样呢?我们只需分“两步走”:第一步,使用三个线程同时执行前三个任务;第二步,再执行第四个显示任务。
通过对比分析,你会发现用单线程执行需要四步,而使用多线程只需要两步。因此,使用并行处理能大大提升性能。
线程 VS 进程
线程不是独立存在的,它是由进程启动和管理的。
一个进程就是一个程序的运行实例。
线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。
线程与进程的关系:
- 进程中任意线程出错,都会导致整个进程崩溃。
- 线程之间共享进程之间的数据。
- 当一个进程关闭时,操作系统会回收进程所用的空间。
- 当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。
- 进程之间的内容相互隔离
- 进程隔离是为了保护操作系统中进程互不干扰的技术,每个进程都只能访问自己占有的数据,避免出现进程A把数据写进进程B的情况,因此一个进程的状态不会影响到其他的进程。
- 如果进程之间需要通信,那么就需要使用进程间通信(IPC)的机制了。
- Electron桌面应用中内部得也是chromium的内核,有两个进程,分别是Main主进程和Renderer渲染进程,两者的通信也是需要用到IPC (inter-process communication)
单进程浏览器时代
- 不稳定
- 复杂功能使用插件机制来实现,但插件的意外崩溃,会导致整个浏览器的崩溃。
- 复杂的JavaScript代码可能导致渲染引擎模块崩溃。
- 不流畅
- 所有页面的渲染,js的运行以及插件,都是运行在同个线程中,意味着同个时刻都只能执行一个模块。
- 容易出现页面的内存泄漏。运行一个复杂一些的页面后关闭,会存在内存无法完全被回收的情况。导致使用时间越长,内存占用越大,浏览器就会变得越慢。
- 不安全
- 插件可以获取到操作系统的任意资源。
- 恶意脚本可以通过浏览器的漏洞来获得系统权限。
多进程浏览器时代
早期多进程架构
- 解决不稳定、不流畅、内存泄漏问题
- 进程相互隔离,只会影响当前页面,关掉当前网页,该进程占用的内存会全部被回收,即可解决问题。
- 解决安全问题
- 使用安全沙箱,程序可以在沙箱中运行,但不能往硬盘写入任何输液架,也无法读取敏感数据,无法获取系统权限。
目前多线程架构
一个浏览器主进程、一个GPU进程、一个网络进程、多个渲染进程和多个插件进程。
- 浏览器进程:主要负责界面显示,用户交互,子进程管理,同时提供存储等功能。
- 渲染进程:将HTML、CSS、JavaScript转化为用户之间可以交互的网页。
- 排版引擎Blink和JavaScript引擎V8都是运行在该进程中。
- 默认情况下,每个Tab标签都是一个进程。
- 处于安全考虑,渲染进程都是运行在沙箱中的。
- GPU进程:初衷是为了实现3D CSS的效果,后面网页、UI界面都是使用GPU来绘制的,所以后续引入了GPU进程。
- 网络进程:主要负责网络资源的加载,之前包含在浏览器进程中。
- 插件进程:负责插件的运行,因为插件容易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
也存在一些问题:
- 更高的资源占用。因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。
- 更复杂的体系架构。浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了。
未来面向服务的架构
原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过 IPC 来通信,从而构建一个 更内聚、松耦合、易于维护和扩展 的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。
同时 Chrome 还提供灵活的弹性架构,在强大性能设备上会以多进程的方式运行基础服务,但是如果在资源受限的设备上(如下图),Chrome 会将很多服务整合到一个进程中,从而节省内存占用。
相关问题
如今的多进程架构,偶尔还会碰到一些由于单个页面卡死最终崩溃导致所有页面崩溃的情况,请问这是什么原因呢
作者回复: 是这样的,通常情况下是一个页面使用一个进程,但是,有一种情况,叫"同一站点(same-site)",具体地讲,我们将“同一站点”定义为根域名(例如,geekbang.org)加上协议(例如,https:// 或者http://),还包含了该根域名下的所有子域名和不同的端口,比如下面这三个:
https://time.geekbang.orghttps://www.geekbang.orghttps://www.geekbang.org:8080 都是属于同一站点,因为它们的协议都是https,而根域名也都是geekbang.org。你也许了解同源策略,但是同一站点和同源策略还是存在一些不同地方,在这里你需要了解它们不是同一件事就行了。
Chrome的默认策略是,每个标签对应一个渲染进程。但是如果从一个页面打开了新页面,而新页面和当前页面属于同一站点时,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫process-per-site-instance。
直白的讲,就是如果几个页面符合同一站点,那么他们将被分配到一个渲染进程里面去。
所以,这种情况下,一个页面崩溃了,会导致同一站点的页面同时崩溃,因为他们使用了同一个渲染进程。
为什么要让他们跑在一个进程里面呢?
因为在一个渲染进程里面,他们就会共享JS的执行环境,也就是说A页面可以直接在B页面中执行脚本。因为是同一家的站点,所以是有这个需求的。