共计 3710 个字符,预计需要花费 10 分钟才能阅读完成。
Inside look at modern web browser 是介绍浏览器实现原理的系列文章,共 4 篇,本次精读介绍第一篇。
尽管本文写于 2018 年,但现在仍然值得学习,因为浏览器实现非常复杂,从细节开始学习很容易迷失方向,不足整体感,而这篇文章从宏观层面开始介绍,简直没有波及代码实现,全都是思路性的形容,非常适合造就对浏览器整体框架性思维。
原文有十分多形象的插图与动图,便于加深对常识的了解,所以也举荐间接浏览原文。
概述
文章先从 CPU、GPU、操作系统开始介绍,因为这些是浏览器运行的基座。
CPU、GPU、操作系统、利用的关系
CPU 即中央处理器,能够解决简直所有计算。以前的 CPU 是单核的,当初大部分笔记电脑都是多核的,业余服务器甚至有高达 100 多核的。CPU 计算能力很强,但只能一件件事解决,
GPU 一开始是为图像处理设计的,即次要解决像素点,所以领有大量并行的解决简略事物的能力,非常适合用来做矩阵运算,而矩阵运算又是计算机图形学的根底,所以大量用在可视化畛域。
CPU、GPU 都是计算机硬件,这些硬件各自都提供了一些接口供汇编语言调用;而操作系统则基于它们之上用 C 语言(如 linux)将硬件治理了起来,包含过程调度、内存调配、用户内核态切换等等;运行在操作系统之上的则是应用程序了,所以应用程序不间接和硬件打交道,而是通过操作系统间接操作硬件。
为什么应用程序不能间接操作硬件呢?这样做有微小的安全隐患,因为硬件是没有任何形象与安全措施的,这意味着实践上一个网页能够通过 js 程序,在你关上网页时间接拜访你的任意内存地址,读取你的聊天记录,甚至读取历史输出的银行卡明码进行转账操作。
显然,浏览器作为一个应用程序,运行在操作系统之上。
过程与线程
为了让程序运行的更平安,操作系统发明了过程与线程的概念(linux 对过程与线程的实现是同一套),过程能够调配独立的内存空间,过程内能够创立多个线程进行工作,这些线程共享内存空间。
因为线程间共享内存空间,因而不需通信就能交换,但内存地址互相隔离的过程间也有通信需要,需通过 IPC(Inter Process Communication)进行通信。
过程之间互相独立,即一个过程挂了不会影响到其它过程,而在一个过程中能够创立一个新过程,并与之通信,所以浏览器就采纳了这种策略,将 UI、网络、渲染、插件、存储等模块过程独立,并且任意挂掉后都能够被从新唤起。
浏览器架构
浏览器能够拆分为许多独立的模块,比方:
- 浏览器模块(Browser):负责整个浏览器内行为协调,调用各个模块。
- 网络模块(Network):负责网络 I/O。
- 存储模块(Storage):负责本地 I/O。
- 用户界面模块(UI):负责浏览器提供给用户的界面模块。
- GPU 模块:负责绘图。
- 渲染模块(Renderer):负责渲染网页。
- 设施模块(Device):负责与各种本地设施交互。
- 插件模块(Plugin):负责解决各类浏览器插件。
基于这些模块,浏览器有两种可用的架构设计,一种是少过程,一种是多过程。
少过程是指将这些模块放在一个或无限的几个过程里,也就是每个模块一个线程,这样做的益处是最大水平共享了内存空间,对设施要求较低,但问题是只有一个线程挂了都会导致整个浏览器挂掉,因而稳定性较差。
多过程是指为每个模块(尽量)开拓一个过程,模块间通过 IPC 通信,因而任何模块挂掉都不会影响其它模块,但害处是内存占用较大,比方浏览器 js 解析与执行引擎 V8 就要在这套架构下拷贝多份实例运行在每个过程中。
Chrome 多过程架构的劣势
Chrome 尽量为每个 tab 独自创立一个过程,所以咱们能力在某个 tab 未响应时,从容的敞开它,而其它 tab 不会受到影响。不仅是 tab 间,一个 tab 内的 iframe 间也会创立独立的过程,这样做是为了爱护网站的安全性。
服务化 – 单 / 多过程弹性架构
Chrome 并不满足于采纳一种架构,而是在不同环境下切换不同的架构。Chrome 将各性能模块化后,就能够自在决定以后将哪些模块放在一个过程中,将哪些模块启动独立过程,即能够在运行时决定采纳哪套过程架构。
这样做的益处是,能够在资源受限的机器上开启单过程模式,以尽量节约内存开销,实际上在手机利用上就是这么做的;而在资源丰盛、内核数量短缺的机器上采纳独立过程模式,尽管耗费了更多资源,但取得了更好的稳定性。
Iframe 独占过程
site-isolation 将同一个 tab 内不同 iframe 包裹在不同的过程内运行,以确保 iframe 间资源的独占性,以及安全性。该性能直到 2018.7 才更新,是因为背地有许多简单的工作要解决,比方开发者工具的调试、网页的全局搜寻性能,都不能因为过程的隔离而受到影响,Chrome 必须让每个过程独自响应这些操作,并最终聚合在一起,让用户感触不到过程间的阻隔。
精读
本文从浏览器如何基于操作系统提供的过程、线程概念构建本人的应用程序开始,从硬件、操作系统、软件的分层开始,介绍到浏览器是如何划分模块的,并且调配过程或线程给这些模块运行,这背地的思考十分有价值。
从宏观角度看,要设计一个平安稳固、高性能、具备拓展性的浏览器,首先要把各功能模块划分分明,并定义好各模块的通信关系,在各业务场景下制订一套模块合作的流程。
浏览器的主从架构
相似应用程序的主从模式,浏览器的 Browser 模块能够看作主模块,它自身用于协调其它模块的运行,并维持其它各模块的失常工作,在其它模块失去响应时期待或从新唤起,或者在模块销毁时进行内存回收。
各从模块也分工明确,比方在浏览器敲击 URL 地址时,会先通过 UI 模块响应用户的输出,并判断输出是否为 URL 地址,因为输出的可能是其它非法参数,或一些查问或设置命令。若输出的的确是 URL 地址,则校验通过后,会告诉 Network 网络模块发送申请,UI 模块就不再关怀申请是如何解决了。Network 模块也是绝对独立的,仅解决申请的发送与接管,如果接管到的是 HTML 网页,则交给 Renderer 模块进行渲染。
有了这些绝对独立且分工明确的模块划分后,将这些模块作为线程或过程治理就都不会影响它们的业务逻辑了,惟一影响的就是内存是否共享,以及某个模块 crash 后是否会影响到其它模块了,所以基于这个架构,判断设施类型,以采纳单过程或多过程模式就变得简略了很多,且这个过程弹性架构自身也不须要入侵各模块业务逻辑,自身就是一套独立的机制。
浏览器作为非常复杂的应用程序,想要继续保护,就必须对每个性能点都进行正当的设计,让模块间高内聚、低耦合,这样才不至于让任何批改牵一发而动全身。
tab、iframe 过程隔离
微前端的沙箱隔离计划也比拟火,这里能够和浏览器 tab/iframe 隔离做个比照。
基于 js 运行时的沙箱计划大多都因为吐槽 iframe 慢而诞生的,个别会基于 with
扭转沙箱代码的上下文,批改拜访的全局对象援用,但基于 js 原型链特色,为了阻断向原型链追溯到主利用代码,个别会采纳 proxy
对 with
mock 的变量进行拜访阻断。
还有一些计划利用创立空 iframe 获取到 document 变量传递给沙箱,肯定水平做到了拜访隔离,且对 document 增加的监听会随 iframe 销毁而销毁,便于管制。
还有一些更加彻底的尝试,将 js 代码扔到 web worker 运行,并通过 mock 模仿了 worker 运行时缺失的 dom API。
比照这些计划能够发现,只有最初 worker 的计划是最彻底的,因为浏览器创立的 worker 过程是齐全资源隔离的,想要和浏览器主线程通信只能利用 postMessage
,尽管有一些基于 ArrayBuffer 的内存共享计划,但因为反对的数据类型具备针对性,也不会存在平安问题。
回到浏览器开发者的视角,为什么 iframe 隔离要花费九牛二虎之力拆分多过程,最初再费很大功夫拼接回来,还原出一个绝对无缝的体验?浏览器厂商其实齐全能够利用下面提到的 js 运行时能力,对 API 语法进行革新,创立一个逻辑上的沙盒环境。
我认为实质起因是浏览器要实现的沙盒必须是过程层面的,也就是对内存拜访权限的相对隔离,因为逻辑层面的隔离可能随着各浏览器厂商实现差别,或 API 自身存在的逻辑破绽而导致越权状况的呈现,所以如果须要结构一个齐全平安的沙盒,最好利用浏览器提供的 API 创立新的过程解决沙盒代码。
总结
本文介绍了浏览器是如何基于操作系统做宏观架构设计的,次要就说了一件事,即对过程,线程模型的弹性应用。同时在 tab、iframe 的设计中也要思考到安全性要求,在必要的时候采纳过程,在浏览器本身模块间因为没有安全性问题,所以可对过程模型进行灵便切换。
探讨地址是:精读《深刻理解古代浏览器一》· Issue #374 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。
关注 前端精读微信公众号
<img width=200 src=”https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg”>
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)