从浏览器原理出发聊聊Chrome插件
浏览器架构演进
单进程浏览器时代
单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。在 2007 年之前,市面上浏览器都是单进程的。
单进程浏览器的架构
很多功能模块运行在一个进程里,是导致单进程浏览器不稳定、不流畅和不安全的一个主要因素。
- 不稳定:早期浏览器需要借助于插件来实现诸如 Web 视频、Web 游戏等各种强大的功能,但是插件是最容易出问题的模块,并且还运行在浏览器进程之中,所以一个插件的意外崩溃会引起整个浏览器的崩溃。除了插件之外,渲染引擎模块也是不稳定的,通常一些复杂的 JavaScript 代码就有可能引起渲染引擎模块的崩溃。和插件一样,渲染引擎的崩溃也会导致整个浏览器的崩溃。
- 不流畅:所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的,这就意味着同一时刻只能有一个模块可以执行。如果一个脚本非常耗时,它就会独占整个线程,这样导致其他运行在该线程中的页面没有机会去执行任务,导致整个浏览器失去响应,变卡顿。
- 不安全:当你在页面运行一个插件时,插件可以操作系统资源,如果是个恶意插件,那么它就可以释放病毒、窃取你的账号密码,引发安全性问题。
多进程浏览器时代
早期架构
2008 年 Chrome 发布时的进程架构
从图中可以看出,早期的架构已经对浏览器的能力进行了拆分,主要拆分为三类:浏览器进程、插件进程和渲染进程。每个页面是运行在单独的渲染进程中的,同时页面里的插件也是运行在单独的插件进程之中,进程之间是通过 IPC 机制进行通信。这就解决了单进程时代浏览器的各种问题:
- 解决不稳定:由于进程是相互隔离的,所以当一个页面或者插件崩溃时,影响到的仅仅是当前的页面进程或者插件进程,并不会影响到浏览器和其他页面。
- 解决不流畅:JavaScript运行在渲染进程中,所以即使JavaScript阻塞了渲染进程,也只会影响当前的渲染页面,并不会影响浏览器和其他页面,因为其他页面的脚本运行在它们自己的渲染进程中。
- 解决不安全:Chrome把插件进程和渲染进程锁在沙箱里面,沙箱里面的程序可以运行,但是不能在硬盘上写入任何数据,也不能在敏感位置读取任何数据,这样即使在渲染进程或者插件进程里面执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限。
近期架构
相较之前,近期的架构又有了很多新的变化。
近期Chrome进程架构
从图中可以看出,最新的 Chrome 浏览器包括:1 个浏览器主进程、1 个 GPU 进程、1 个网络进程、多个渲染进程和多个插件进程。
- 浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。可以理解浏览器进程是一个统一的"调度大师"去调度其他进程,比如我们在地址栏输入url时,浏览器进程首先会调用网络进程。
- 渲染进程:核心任务是将HTML、CSS和JavaScript转换为用户可以交互的网页,排版引擎Blink和JavaScript引擎V8都是运行在该进程中,默认情况下,Chrome会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
- GPU进程:其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
- 网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
- 插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
当前架构
目前Chrome浏览器的架构正在发生一些改变,称为面向服务的架构(SOA),目的是将和浏览器本身(Chrome)相关的部分拆分为一个个不同的服务,服务化之后,这些功能既可以放在不同的进程里面运行也可以合并为一个单独的进程运行。这样做的主要原因是让Chrome在不同性能的硬件上有不同的表现。当Chrome运行在一些性能比较好的硬件时,浏览器进程相关的服务会被放在不同的进程运行以提高系统的稳定性。相反如果硬件性能不好,这些服务就会被放在同一个进程里面执行来减少内存的占用。
面向服务的架构
插件运行机制
在运行机制前,我们先来回顾一下打开页面会发生什么:
打开页面发生了什么
- 用户新增一个tab,此时系统浏览器进程、渲染进程、GPU 进程、网络进程会被创建好;
- 用户输入url,浏览器进程检查url,组装协议,构成完整的url;
- 浏览器进程通过进程间通信(IPC)把url请求发送给网络进程;
- 网络进程接收到url请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程;
- 如果没有,网络进程向web服务器发起http请求(网络请求);
- 网络进程解析响应流程;
- 检查状态码,非200执行状态码对应的处理逻辑;
- 200响应处理:检查响应类型Content-Type,如果是字节流类型,则将该请求提交给下载管理器,不再进行后续的渲染,如果是html则通知浏览器进程准备渲染进程进行渲染;
- 准备渲染进程
- 浏览器进程检查当前url是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程;
- 传输数据、更新状态
- 渲染进程准备好后,浏览器向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”;
- 渲染进程接收完数据后,向浏览器发送确认消息;
- 浏览器进程接收到确认消息后更新浏览器界面状态:安全、地址栏url、前进后退的历史状态、更新web页面;