chrome架构
多进程
最上层是浏览器进程,负责协调承担各项工作的其他进程,比如实用程序进程、渲染器进程、GPU进程、插件进程等,如下图所示。
渲染器进程对应新开的标签页,每开一个标签,就会创建一个新的渲染器进程,Chrome还会尽量给每个站点新开一个渲染器进程,包括iframe中的站点,以实现站点隔离。
- 浏览器进程:控制浏览器这个应用的chrome(主框架)部分,包括地址栏、书签、前进/后退按钮等,同时也会处理浏览器不可见的高权限任务,如发送网络请求、访问文件。
- 渲染器进程:负责在标签页中显示网站及处理事件。
- 插件进程:控制网站用到的所有插件。
- GPU进程:在独立的进程中处理GPU任务。之所以放到独立的进程,是因为GPU要处理来自多个应用的请求,但要在同一个界面上绘制图形。
当然,还有其他进程,比如扩展进程、实用程序进程。要知道你的Chrome当前打开了多少个进程,点击右上角的按钮,选择“更多工具”,再选择“任务管理器”。
多进程架构优点
如果一个标签页崩溃了,由于是多进程架构,不会影响别的标签页,只要把这个崩了的标签页关了就行
webkit被吐槽内存泄露,多进程很大程度避免内存泄露,一个进程关了,内存就被回收了,谷歌发文章鄙视过说多进程占用内存多的人
除此之外,还是为了安全和隔离
os有限制进程特权的机制,可以限制进程的能力,chrome可以限制处理用户输入的渲染器进程无法访问文件。
由于进程都有自己的私有内存空间,因此进程可能都会保存某个公共基础设施的副本,比如chrome的v8引擎,这会大大增大内存开销,所以chrome会限制自己能打开的进程数量,根据机器的性能来考量,超过这个进程数量后,会用一个进程管理同一个站点的多个标签页
重点说一说站点隔离(t.cn/RgNAwLC)。站点隔离是新近引入Chrome的一个里程碑式特性,即每个跨站点iframe都运行一个独立的渲染器进程。即便像前面说的那样,每个标签页单开一个渲染器进程,但允许跨站点的iframe运行在同一个渲染器进程中并共享内存空间,那安全攻击仍然有可能绕开同源策略(t.cn/8s1ySzx),而且有人发现在现代CPU中,进程有可能读取任意内存(t.cn/R8FwHoX)。
进程隔离是隔离站点、确保上网安全最有效的方式。Chrome 67桌面版默认采用站点隔离。站点隔离是多年工程化努力的结果,它并非多开几个渲染器进程那么简单。比如,不同的iframe运行在不同进程中,开发工具在后台仍然要做到无缝切换,而且即便简单地Ctrl+F查找也会涉及在不同进程中搜索。
如果页面不多不复杂的话,其实单进程还是很有性能和内存优势的。至少我看了报道,单个页面的话,单进程比多进程要性能高5-10%左右
windows下多进程的负面影响很大,因为没有linux的fork的COW(写时复制)机制
COW(写时复制)
fork子进程,子进程的地址空间指向父进程的地址空间,把内存空间设置为readonly,即父子进程共享一块内存空间,读的时候相安无事,写的时候会触发分页中断(page-fault),这个时候来复制
优点
减少分配和复制资源的延迟,减少不必要的资源分配
缺点
大量写操作,会产生大量的分页错误
导航
导航涉及浏览器进程与线程间为显示网页而通信。一切从用户在浏览器中输入一个URL开始。输入URL之后,浏览器会通过互联网获取数据并显示网页。从请求网页到浏览器准备渲染网页的过程,叫做导航。
如前所述,标签页外面的一切都由浏览器进程处理。浏览器进程中有线程(UI线程)负责绘制浏览器的按钮和地址栏,有线程(网络线程)负责处理网络请求并从互联网接收数据,有线程(存储线程)负责访问文件和存储数据。
处理输入
search query or URL?
开始导航
UI线程通知网络线程,标签页转圈圈,网络线程dns->tls 可能会有重定向
读取响应
网络线程:有服务端响应体的content-type来确定加载的数据类型,如果没有content-type,需要MIME类型嗅探
html文件->渲染器进程
压缩包啥的->下载管理器
此时也是安全浏览检查的环节,如果是匹配的恶意网站,网络线程会显示警告页
联系渲染器进程
网络线程通知UI线程数据准备好了->UI线程联系渲染器进程渲染网页
优化策略:UI线程给网络线程发送请求后,其实往往知道要导航的网站是哪个了,所以会提前启动一个渲染器进程,来待命,发生了重定向可能就不需要这个渲染器进程
提交导航
数据和渲染器进程都有了,通过IPC 浏览器进程向渲染器进程提交导航(也就是开始渲染),渲染器进程会同时收到不间断的html数据流
此时,地址栏会更新,安全指示图标和网站设置UI也会反映新页面的信息。当前标签页面的会话历史会更新,后退/前进按钮起作用。为便于标签页/会话在关闭标签页或窗口后恢复,会话历史会写入磁盘。
初始加载完成
加载完成之后,渲染器进程通过IPC给浏览器进程发送一个消息,此时UI线程停止标签页上面的旋转图标。 如果此时再跳转新的网站(超链接 window.location),会开始新的一轮导航请求,不过不同的就是这个导航请求是渲染进程交给浏览器进程的
要注意的是,要看js代码里有没有关注beforeonload事件