从浏览器层面看前端性能:了解 Chrome 组件、多进程与多线程
一、 前言
现阶段的浏览器运行在一个单用户,多合作,多任务的操作系统中。一个糟糕的网页同样可以让一个现代的浏览器崩溃。其原因可能是一个插件出现bug,最终的结果是整个浏览器以及其他正在运行的标签被销毁。
现代操作系统已经非常健壮了,它让应用程序在各自的进程中运行和不会影响到其他程序。一个进程崩溃不会损害到其他进程以及操作系统。同时系统会严格的限制一个用户访问另外一个用户空间的数据。
浏览器属于一个应用程序,而应用程序的一次执行,可以理解为计算机启动了一个进程,进程启动后,CPU会给该进程分配相应的内存空间,当我们的进程得到了内存之后,就可以使用线程进行资源调度,进而完成我们应用程序的功能。
而在应用程序中,为了满足功能的需要,启动的进程会创建另外的新的进程来处理其他任务,这些创建出来的新的进程拥有全新的独立的内存空间,不能与原来的进程内向内存,如果这些进程之间需要通信,可以通过IPC机制(Inter Process Communication)来进行。
假如我们去开发一个浏览器,它的架构可以是一个单进程多线程的应用程序,也可以是一个使用IPC通信的多进程应用程序。
以chrome为例,使用IPC通信的多进程应用程序
chrome浏览器与其他浏览器不同,chrome使用多个渲染引擎实例,每个Tab页一个,即每个Tab都是一个独立进程。
二、浏览器组件
浏览器大体上由以下几个组件组成,各个浏览器可能有一点不同。
-
界面控件 – 包括地址栏,前进后退,书签菜单等窗口上除了网页显示区域以外的部分
-
浏览器引擎 – 查询与操作渲染引擎的接口
-
渲染引擎 – 负责显示请求的内容。比如请求到HTML, 它会负责解析HTML、CSS并将结果显示到窗口中
-
网络 – 用于网络请求, 如HTTP请求。它包括平台无关的接口和各平台独立的实现
-
UI后端 – 绘制基础元件,如组合框与窗口。它提供平台无关的接口,内部使用操作系统的相应实现
-
JS解释器 - 用于解析执行JavaScript代码
-
数据存储持久层 - 浏览器需要把所有数据存到硬盘上,如cookies。新的HTML5规范规定了一个完整(虽然轻量级)的浏览器中的数据库web database
三、Chrome的并发模型
chrome的进程,chrome没有采用一般应用程序的单进程多线程的模型,而是采用了多进程的模型,按照他的文字说明,主界面框架下的一个TAB就对应这个一个进程。但实际上,一个进程不仅仅包含一个页面,实际上同类的页面在共用一个进程。
Google在宣传的时候一直都说,Chrome是one tab one process的模式。实际上,Chrome支持的进程模型远比宣传丰富,简单的说,Chrome支持以下几种进程模型:
-
Process-per-site-instance:就是你打开一个网站,然后从这个网站链开的一系列网站都属于一个进程。这是Chrome的默认模式。
-
Process-per-site:同域名范畴的网站放在一个进程,比如www.google.com和www.google.com/bookmarks就属于一个域名内(google有自己的判定机制),不论有没有互相打开的关系,都算作是一个进程中。用命令行–process-per-site开启。
-
Process-per-tab:这个简单,一个tab一个process,不论各个tab的站点有无联系,就和宣传的那样。用–process-per-tab开启。
-
Single Process:这个很熟悉了吧,即传统浏览器的模式:没有多进程只有多线程,用–single-process开启。
四. 多进程的好处
把渲染放到另外个进程防止崩溃了影响主进程。webkit最初时候很多内存泄露。多进程能很大程度避免。一个进程关了,所有内存就回收了。其次,多进程安全性更好。如果blink被发现什么提权漏洞,例如写一段js就能控制整个chromium进程做任何事情,显然多进程可以把损失限制在渲染线程。渲染线程拿不到主进程的各种私密信息,例如别的域名下的密码。
五. 多线程模型
chrome进程模型下有
1.Browser进程只有一个,主控整个系统的运行,管理Chrome大部分的日常事务;
-
负责浏览器页面的显示,各个页面的管理,所有其他类型进程的祖先,负责他们的创建和销毁。
2.UI thread:
-
负责浏览器界面显示,与用户交互。如前进,后退等,将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
-
network thread:网络资源的管理,下载等(这个和网络进程间???)
-
storage thread:控制文件等的访问;
3.Renderer 浏览器渲染进程(浏览器内核), 主要负责页面的渲染和显示:页面渲染,脚本执行,事件处理等。默认每个Tab页面一个Renderer进程(Renderer进程,内部是多线程的)
4.Utility Network:网络进程,负责页面网络资源的加载
5.GPU进程:最多只有一个,GPU硬件加速打开时才会被创建,用于3D绘制等。
6.NPAPI或PPAPI插件进程,每种类型的插件对应一个进程,仅当使用该插件时才创建。
1995 年 Netscape 发明了NPAPI (Netscape plugin API)这个种架构,来帮助浏览器渲染一些HTML没有的东西。比如 PDF, 比如 视频, 以及等等。NPAPI不限制插件自由访问系统所有的API,而且和浏览器是平级运行的。现在已被禁用。PPAPI是谷歌提出的架构。
7.Pepper插件进程
8.其他类型的进程,比如Linux的Zygote进程;Sandbox进程。
Browser作为主进程最先启动,Browser包含一个主线程mainthread,在mainthread中对整个系统进行初始化,并启动为另外几个线程,看下面的代码:
void CreateChildThreads(BrowserProcessImpl* process) {
process->db_thread(); //负责数据库处理
process->file_thread(); // 负责文件管理
process->process_launcher_thread();
process->cache_thread(); //负责管理缓存
process->io_thread(); //负责管理进程间通信和所有I/O行为。
}
io_thread不仅负责Browser进程的I/O,而且其他Renderer的I/O请求也会通过进程间通信发送到这个线程,由该线程进行处理,最后把结果在返回给各个Renderer进程。各个线程的功能不一样,但设计模式是一样的
对于Renderer进程,它们通常有两个线程:一个是Main thread,负责与主线程联系。另一个是Render thread,它们负责页面的渲染和交互
当我们是要浏览一个网页,我们会在浏览器的地址栏里输入URL,这个时候Browser Process会向这个URL发送请求,获取这个URL的HTML内容,然后将HTML交给Renderer Process,Renderer Process解析HTML内容,解析遇到需要请求网络的资源又返回来交给Browser Process进行加载,同时通知Browser Process,需要Plugin Process加载插件资源,执行插件代码。解析完成后,Renderer Process计算得到图像帧,并将这些图像帧交给GPU Process,GPU Process将其转化为图像显示屏幕。