一文带你了解浏览器的进化史

浏览器已经成为人们生活中的必备工具了,作为前端开发人员,dom tree、js解析编译、cssom、渲染树、各种进程间的作用和协作等等都是朗朗上口,但是我们真的了解浏览器吗?你知道浏览器的完整架构吗?你知道浏览器为什么要这么设计吗?今日的高速浏览器并不是一开始就这样的,浏览器随着互联网的发展,在这几十年间进行了多次演化,最终演变成现在的架构,当然,演化还在持续进行。

浏览器简史

我们先来了解下浏览器的发展史。自1969年阿帕网(ARPANET)正式投入运行,引发了计算机网络的一场革命,到1984年NSFNET,到1990年蒂姆·伯纳斯-李(Tim Berners-Lee)建立了超文本传输协定(HTTP)、超文本标记语言(HTML)、第一个网页浏览器、第一个网页服务器和第一个网站,并开始推行WorldWideWeb(万维网)项目。此后几十年,围绕计算机网络研发的技术不断的发展,这些技术中,浏览器无疑是最耀眼的发明之一。 1993年,计算机科学家马克·安德森(Marc Andreessen)开发了 Mosaic,这是第一款流行的 Web 浏览器,也是 Mozilla Firefox 的早期版本。 1994年, Netscape(网景)公开发行了 Netscape Navigator,这是第一款面向普通用户的浏览器。 1995年,微软拿到了Mosaic旧版代码的授权,研发出自己的浏览器Internet Explorer,浏览器之争自此引发。同年,网景设计并推出JavaScript,让网站拥有自运算的能力。 1997年,微软推出IE4.0,并将windows系统和IE浏览器做了捆绑,这一举动让微软在4年内获得了75%的市场份额,其中1999年,市场份额高达99%。 1998年,Opera入场 2002年,Firefox问世 2003年,Safari问世,并内置到苹果的MacOS和IOS系统 2008年,Chrome问世,并带来了谷歌的开源项目:Chromium 自此浏览器市场进入,群雄逐鹿的时代,当然其中还是IE一家独大,IE成为了前端开发心目中的噩梦(史上最坑爹的IE6),直到2010年,IE的市场份额才降低到50%。之后,各大浏览器厂商不断推陈出新,在技术、产品上不断角力。此后10年,随之移动互联网的发展,各大厂商也推出了自己的移动端浏览器。到了2023年的今天,浏览器市场也发生了翻天覆地的变化,Chorme崛起,IE扑街。下图为最近一年的浏览器市场份额:

浏览器顶层结构

本文只讨论Chrome浏览器的架构(其他的也差不多,更多差异是在实现细节上)。 一个浏览器,大致会包含以下几个组件:

  • 用户界面:包括地址栏、前进后退按钮、书签等等
  • 浏览器引擎:中间的一层,负责渲染引擎到UI的指令传输,其实就是查询和操作渲染引擎的接口。
  • 渲染引擎:负责显示请求的内容(例如将HTML和CSS解析后呈现)
  • 网络模块:负责执行网络请求,因为各个平台的网络接口有差异,该模块也会根据不同的平台做不同的后台实现,保证在前台抹平差异
  • 界面后台服务(UI backend/Display Backend):用于绘制组合框和窗口等基本小部件,这个模块也提供了跨平台的统一接口
  • JavaScript解析器:用于编译和执行js
  • 数据存储:为浏览器提供持久化的存储能力,例如:存储cookie,该模块也为浏览器的存储能力提供支持:localStorage、IndexedDb、WebSQL和FileSystem

架构简图:

进程下的浏览器

浏览器的这些基础结构是怎么结合到一起的呢?浏览器内部是怎么运作的呢?这个得从浏览器的进程讲起。

进程(Process)和线程(Thread)

百度百科上进程的描述:

计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础

简单理解就是进程就是一个应用程序的执行程序。 关于线程的描述:

操作系统能够进行运算调度的最小单位。 它被包含在进程之中,是进程中的实际运作单位。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

线程就是存在于进程内部,并执行其所属进程程序的部分功能 按照进程的定义,浏览器的这些功能既可以拆解成多个独立的进程独立运行,再通过IPC进行通讯,或者也可以将整个浏览器都放在一个进程中运行,不同的功能再通过不同的线程来执行。

单进程模式下的浏览器

如图所示,单进程模式下,浏览器的所有组件都跑在一个进程下,通过多个功能性线程来运转,2007年以前的浏览器大部分都是这种模式。在这种模式下,任何行为都需要小心翼翼,因为任何一个线程崩溃都会导致整个进程崩溃,引发浏览器的崩溃。 在早期的互联网环境中,单进程其实是没有问题的,那时候网站能力没那么多,大部分是静态资源的浏览和资源下载,浏览器本身不需要承载太多功能,这样在开发商不断地调优下,单进程浏览器也是可以运行的很好的。当然这只是理论上的,也是单进程的第一个问题:卡顿/不流畅

卡顿/不流畅

尽管开发商可以让浏览器的各个线程的协作趋向完美,但是开发商始终无法控制在浏览器上运行的网站代码。例如我们打开一个站点,这个站点的缺心眼开发人员写了一段这样的js代码:

javascript 复制代码
var bool = true;
while(bool) {
  console.log(1);
}

这段js一执行,内存溢出,线程崩溃,浏览器进程直接卡死,其他页面也会卡顿或卡死。 同样的道理,假如某个页面打开一个复杂的站点,这个站点有大量的请求,大量的js计算,那么整个浏览器的所有标签页都会卡顿的。另外早期的浏览器的内存回收机制也是有漏洞的,打开一个大型站点,然后再关闭,可能会出现内存未回收的情况持续占用进程的内存(每个进程的内存是有限的,一般会在进程启动的时候就分配好固定的内存空间),可用内存被压缩,也会卡顿不流畅。

稳定性问题

早期的技术不够成熟,单纯通过浏览器无法实现很多场景,例如:在线聊天(可以通过ajax实现,但是效果非常一般,会大量占用网络线程,而且还不够灵敏)、视频播放、游戏等等,所以这些功能都需要借助插件来实现,插件是运行在渲染线程里面的,插件一出问题,线程就可能崩溃,当年的flash插件崩溃导致整个浏览器挂掉的场景还历历在目。另外,复杂的js代码也可能导致渲染线程挂掉。

安全问题

以前的浏览器插件(特别是IE的插件),可以通过其他高级语言开发,这些插件可以不受浏览器的限制直接访问原生系统的能力,那么这些插件(特别是非官方渠道下载的插件)就有非常大的安全隐患。早年间一些木马、病毒就是通过浏览器插件来传播的。 另外一个问题就是页面脚本,这些脚本会通过浏览器的漏洞获取到系统权限,再通过系统权限下载或植入病毒木马等等恶意的东西。我以前就遇到过,有客户反馈网站打不卡,后面到现场帮他排查就是IE6的浏览器打开了某个站点,该站点通过漏洞植入了一个默认脚本,这个脚本会在浏览器打开的页面里面植入一段js,js在执行的时候会将页面重定向到广告站点。 安全问题另说,稳定和流畅度问题,在互联网快速发展的浪潮中,单进程浏览器明显不符合时代的需求,满足不了对浏览器要求越来越高的网民的诉求,这时候多进程浏览器也应运而生。

多进程浏览器

相对于单进程,多进程(mutli process)就是把原来在线程中执行的部分程序放到独立的进程中执行,进程间通过IPC通讯,下图很好的表示单进程浏览器和多进程浏览器的区别: 但是这个架构会有一些问题:

  1. 切割成多个进程后,线程间通过进程即可共享数据,而进程间需要通过IPC进行通讯,架构的复杂度会变高很多,同时也要处理大量的IPC异常问题
  2. 如果只是简单的把所有功能模块都划分到不同的进程中去执行,会导致部分进程过于膨胀,例如渲染进程,在打开很多个页面的时候,进程就会趋向饱和,同样有崩溃的风险。

看看Chrome是怎么设计这个架构的。

初代多进程浏览器

下图是2008年Chrome的多进程架构设计 从架构简图可以看出,进程被划分成一个浏览器主进程,用于全局控制,然后会有多个渲染进程、插件进程,此外还有调度系统能力的底层进程。 我们来看看多进程是怎么解决单进程遇到的问题。 多进程,这样就可以把不同的页面(窗口/签页)和插件在底层就隔离开,那么某个页面或插件出现问题,只会影响到当前的渲染进程不会影响整个浏览器。这样就避免了不稳定性的问题。 如上图,第一个tab崩溃了,但是第二个tab依然可以正常运行。 在这个架构中,js是运行在各自的渲染进程里面,这样不管js引起的卡顿问题也只会影响当前进程,不会影响其他页面。至于前面说到的内存泄漏的问题也是一样的,当页面被关闭时,进程销毁,内存回收,也就没有内存泄漏的问题了。 安全上,采用了多进程的就可以使用安全沙箱,简单理解安全沙箱就是为进程上了锁,程序可以运行,但是不能直接访问系统,不能往硬盘写入内容,不能读取受限制的数据,也就是说渲染进程和插件进程内执行的程序会直接受到浏览器的限制。这样渲染进程中的恶意程序就无法渗透到系统。这是后可能有人会有疑问,单进程为什么不能这么玩?因为单进程就一个进程,锁了这个进程那么浏览器本身也无法访问系统,那就无法使用了,而且谁来锁这个浏览器的进程呢?那单进程沙箱不行,线程沙箱可不可以?据我所知,没有线程沙箱这个能力。 由于安全沙箱的存在,假如js或者插件需要访问到原生系统的能力,就要通过常见的桥接的方式来实现。 其实这里还有一个疑问,我们都知道不管pc的配置多高,资源总是有限的,系统允许浏览器使用的资源数量也是有限的,那假如一直打开新页面,是不是浏览器也可能会爆掉?答案是肯定的,不过日常一般不会遇到,另外浏览器会将同一来源的页面放到同一个渲染进程里面,节省资源。这也是有时候打开一个站点的两个页面,会同时一起崩溃的原因。

新的多进程架构

相对于初代多进程架构,新的多进程架构做了一些调整,一个是使用GPU,另一个是讲网络进程独立出来。

GPU进程: Chrome主要通过这个进程来进行图形和视觉处理(硬件加速主要是通过这进程来完成),该进程会接收来自其他进程的CPU任务。正常的情况下,应用程序使用CPU或GPU是通过操作系统提供的机制来完成,应用程序不会直接感知硬件的存在,而Chrome这个GPU进程一般也不会被使用,仅在浏览器在网页上渲染视频或图片时使用,可以打开Chrome的任务管理器观察,如果页面是没有视频或图片,GPU进程占用会降到0%。

网络进程: 负责网路资源的加载和网络操作,网络进程独立出来,个人认为是因为互联网的快速发展,B/S架构快速的演化,站点越来越大,用户对于前端的体验要求越来越高(特别是H5全面覆盖后),对于浏览器的网络能力的要求更高,将网络从浏览器进程中独立出来,有更多的操作空间,也可以尽可能避免大量的网络请求影响浏览器进程。

渲染进程: 核心进程,负责站点的呈现,Chrome的排版引擎Blink和JavaScript引擎V8都是跑在这个进程里面,默认情况下,Chrome会为每个tab创建一个渲染进程(通过复制出来的tab会在一个渲染进程中),这些进程都是在安全沙箱中运行,保证安全。

复制出来的tab: 通过url打开的相同站点的tab: 插件进程: 独立运行插件,避免插件崩溃影响浏览器,而且每个插件是一个独立进程,避免插件互相影响。 在这一时期的Chrome中,打开一个页面,至少会有四个进程:浏览器主进程、GPU进程、网络进程、渲染进程。如果有插件,还会有插件的进程,

特殊的渲染进程

2018年,Chrome发布了站点隔离(Site isolation)功能,主要是针对安全漏洞问题。在之前的Chrome中,使用iframe将不同域的站点嵌套在一起,这两个站点会使用同一个渲染进程,也就是这些站点是共用一个内存空间,那么就可以通过Meltdown 和 Spectre,绕过同源策略或者是通过伪造站点+iframe的方式窃取信息等等。 站点隔离功能,会强制将同一个页面上不同站点的iframe用多个独立渲染进程进行渲染,将这些非同源的站点强制隔离。

多进程的问题

架构复杂化度增加 ,这个是不变的定理,架构中的组件模块越多,相互间的交互就会越复杂,耦合、扩展性等问题都会影响整个架构 资源问题,多进程架构相对来说会占用更多的资源,打开一个页面就有4个进程,在硬件条件一般的设备上就会很尴尬。例如:医院的自助机器有部分是用混合app的方式开发(为什么我会知道,因为每次都能在付费页面看到NaN的字样,还有打出来的小票上每次都有一项undefined),这些终端的硬件条件一般,虽然只处理单一的功能,但是依然会因为复杂的流程导致卡顿等问题(经常出现刷社保卡卡住的)。

SOA面向服务的浏览器架构

Chrome团队在2016年提出的面向服务的架构(SOA:Service-Oriented Architecture),将一些基础能力拆解成一个个服务,架构大致如下: 这么划分,每个服务都可以在一个独立的进程中运行,每个服务定义好标准接口,通过IPC进行标准化通讯,通过这种模式可以构建一个高内聚低耦合的系统,更便于维护和扩展,使Chrome更轻、更简单、更高效。而且在这个服务化的架构下,Chrome可以根据当前设备的可用资源,选择性的将服务聚合在一个进程中节省资源或者将服务独立在进程中运行提高性能。 目前,Chrome正在向这个SOA的架构持续演进,这会是一个比较漫长的过程。 Chrome 109.0.5414.119版本下的进程:

总结

从单进程到多进程到服务化,浏览器的架构也是随着硬件、操作系统、用户需求、新技术的发展不断的向前演化,这些演化也不是一蹴而就,是一个漫长的持续过度的过程。从整个浏览器的进化史,我们也能了解到时代的变化带来机遇,前端由简单的页面展示到现在覆盖多端多设备,越来越多的业务和应用选择在web上实现,浏览器的进化起到至关重要的作用。而今后,浏览器还会不断的演进,满足更多的需求,覆盖更多的场景,例如:当下的VR、Aiot等等。作为以浏览器为主的前端人员,我们应该紧跟时代脚步,不要错过这波红利。

参考文献

developer.chrome.com/blog/inside... developer.chrome.com/blog/inside... developer.chrome.com/blog/inside... developer.chrome.com/blog/inside... web.dev/howbrowsers... thekoalashome.files.wordpress.com/2018/10/a1-...

相关推荐
MickeyCV22 分钟前
Nginx学习笔记:常用命令&端口占用报错解决&Nginx核心配置文件解读
前端·nginx
祈澈菇凉40 分钟前
webpack和grunt以及gulp有什么不同?
前端·webpack·gulp
zy0101011 小时前
HTML列表,表格和表单
前端·html
初辰ge1 小时前
【p-camera-h5】 一款开箱即用的H5相机插件,支持拍照、录像、动态水印与样式高度定制化。
前端·相机
HugeYLH1 小时前
解决npm问题:错误的代理设置
前端·npm·node.js
六个点2 小时前
DNS与获取页面白屏时间
前端·面试·dns
道不尽世间的沧桑2 小时前
第9篇:插槽(Slots)的使用
前端·javascript·vue.js
bin91532 小时前
DeepSeek 助力 Vue 开发:打造丝滑的滑块(Slider)
前端·javascript·vue.js·前端框架·ecmascript·deepseek
uhakadotcom2 小时前
最新发布的Tailwind CSS v4.0提供了什么新能力?
前端
GISer_Jing2 小时前
Node.js中如何修改全局变量的几种方式
前端·javascript·node.js