浏览器进程
基于Chrome
浏览器学习浏览器进程架构
查看Chrome浏览器启动的进程
点击Chrome
浏览器右上角的“选项”菜单,选择“更多工具”子菜单,点击“任务管理器”,这将打开Chrome
的任务管理器的窗口,如下图:
什么是进程?什么是线程?
进程
进程: 指在系统中正在运行的一个应用程序,程序一旦运行就是进程。或者更专业化来说:进程是指程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
线程
线程: 系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。进程——资源分配的最小单位,线程——程序执行的最小单位。
进程与线程的区别
- 因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。
- 体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。。
- 体现在CPU系统上面,线程使得CPU系统更加有效,因为操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
- 体现在程序结构上,举一个简明易懂的列子:当我们使用进程的时候,我们不自主的使用if else嵌套来判断pid,使得程序结构繁琐,但是当我们使用线程的时候,基本上可以甩掉它,当然程序内部执行功能单元需要使用的时候还是要使用,所以线程对程序结构的改善有很大帮助。
进程与线程之间的关系
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
什么情况下使用进程? 什么情况下使用线程?
- 需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的
- 线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应
- 因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布用线程
- 并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求
- 需要更稳定安全时,适合选择进程;需要速度时,选择线程更好
Chrome 多进程架构
从图中可以看出,最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。
浏览器进程
浏览器进程 主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
渲染进程
渲染进程 核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。渲染进程包含的线程如下:
- GUI渲染线程
- 负责渲染页面,布局和绘制
- 页面需要重绘和回流时,该线程就会执行
- 与js引擎线程互斥,防止渲染结果不可预期
- JS引擎线程
- 负责处理解析和执行javascript脚本程序
- 只有一个JS引擎线程(单线程)
- 与GUI渲染线程互斥,防止渲染结果不可预期
- 事件触发线程
- 用来控制事件循环(鼠标点击、setTimeout、ajax等)
- 当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中
- 定时触发器线程
- setInterval与setTimeout所在的线程
- 定时任务并不是由JS引擎计时的,是由定时触发线程来计时的
- 计时完毕后,通知事件触发线程
- 异步http请求线程
- 浏览器有一个单独的线程用于处理AJAX请求
- 当请求完成时,若有回调函数,通知事件触发线程
当我们了解了渲染进程包含的这些线程后,我们思考两个问题:
-
为什么 javascript 是单线程的?
首先是历史原因,在创建
Javascript
这门语言时,多进程多线程的架构并不流行,硬件支持并不好。其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。而且,如果同时操作DOM
,在多线程不加锁的情况下,最终会导致DOM
渲染的结果不可预期。 -
为什么 GUI 渲染线程与 JS 引擎线程互斥
这是由于
JavaScript
是可以操作DOM
的,如果同时修改元素属性并同时渲染界面(即 JS线程和UI线程同时运行), 那么渲染线程前后获得的元素就可能不一致了。因此,为了防止渲染出现不可预期的结果,浏览器设定GUI
渲染线程和JavaScript
引擎线程为互斥关系, 当JavaScript
引擎线程执行时GUI
渲染线程会被挂起,GUI
更新则会被保存在一个队列中等待JS引擎线程空闲时立即被执行。
GPU 进程
GPU 进程 其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
网络进程
网络进程 主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
插件进程
插件进程 主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
多进程问题
-
对于浏览器多进程的思考: 浏览器多进程模型提升了浏览器的稳定性、流畅性和安全性,但同样不可避免地带来了一些问题:
- 更高的资源占用 因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。
- 更复杂的体系架构 浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了。
-
浏览器多进程的优势:
- 避免单个page crash影响整个浏览器
- 避免第三方插件crash影响整个浏览器
- 多进程充分利用多核优势
- 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性
未来面向服务的架构
为了解决更高的资源占用以及更复杂的体系架构的问题,在 2016 年,Chrome
官方团队使用“面向服务的架构”(Services Oriented Architecture,简称 SOA)的思想设计了新的 Chrome 架构。也就是说 Chrome
整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过IPC来通信,从而构建一个更内聚、松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。
理解: 以前是每个页面需要若干进程完成各自的工作,现在是将各个页面通用的功能(视频、网络、渲染等)发布为系统服务,页面在需要的时候与相应的服务通信完成需要的功能。这起码把进程间的耦合从页面中分离出去了。如下图所示:
思考
-
回顾浏览器的进化路线,你认为推动浏览器发展的主要动力是什么?
互联网的兴起,web应用程序可快速开发的特性以及可访问性。倒逼浏览器厂商不断进行以适应市场环境的变化。