基于一个高频面试题‘浏览器地址栏输入到展示发生了什么’引发的深入思考;如果要很好的回答这个问题,需要比较全面地掌握计算机很多知识,其中涉及到了网络、操作系统、Web等一系列的知识。这次通过浏览器底层原理系列分析来更好地回答这个问题。
主要通过以下四个系列学习浏览器原理:
针对问题‘浏览器地址栏输入到展示发生了什么’可能我们很多人的脑海中第一印象是如下图所示答案比较发散:
实际如果有关注过Chrome浏览器的任务管理系统,我们可以发现浏览器打开一个标签页面会启动四个进程
基于这四个进程我们可以重新梳理发散的答案,对这些答案进行收敛如下图所示
通过简单收敛我们可以发现学习浏览器原理只要学习这四类进程之间在做什么进行归类学习。
学习之前可以追溯历史看看浏览器的发展历史,方便我们在后续学习中针对遇到的一些坑
可以找到历史的影子。
进程可以被描述为一个应用的执行程序,线程存在于进程并执行任意部分。操作系统为进程提供了一块可以使用的内存[数据]
,应用的所有状态都保存在该私有内存空间中,关闭应用,进程会关闭,操作系统释放内存
。
进程如下图所示:
如图所示单独的进程中包含了可执行代码、内存数据、操作系统文件等
进程具有的特征:
早期的操作系统中并没有线程的概念,后来随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。
从上图可以看出,线程 1、线程 2、线程 3 分别把执行的结果写入 A、B、C 中,然后线程 2 继续从 A、B、C 中读取数据,用来显示执行结果
单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。在2007年之前的浏览器都基本是单进程浏览器。
可以看出只有一个进程(progress)
, 多个功能模块都是通过多个线程
并行处理任务实现。
早起浏览器都是通过第三方插件
来实现诸如 Web 视频、Web 游戏等各种强大的功能,但是插件是最容易出问题的模块。这些插件以线程运行在浏览器,一个插件的意外崩溃会引起整个浏览器的崩溃。
渲染引擎
模块也是不稳定的。通常一些复杂的 JavaScript 代码就有可能引起渲染引擎模块的崩溃。
从上面的“单进程浏览器架构示意图”可以看出,所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的,这就意味着同一时刻只能有一个模块可以执行
function freeze() { while (1) { console.log("freeze"); } }; freeze(); 复制代码
因为这个脚本是无限循环的,所以当其执行时,它会独占整个线程,这样导致其他运行在该线程中的模块就没有机会被执行。因为浏览器中所有的页面都运行在该线程中,所以这些页面都没有机会去执行任务,这样就会导致整个浏览器失去响应,变卡顿。
页面的内存泄漏也是单进程变慢的一个重要原因。通常浏览器的内核都是非常复杂的,运行一个复杂点的页面再关闭页面,会存在内存不能完全回收的情况,这样导致的问题是使用时间越长,内存占用越高,浏览器会变得越慢。
插件可以使用 C/C++ 等代码编写,通过插件可以获取到操作系统的任意资源,当你在页面运行一个插件时也就意味着这个插件能完全操作你的电脑。如果是个恶意插件,那么它就可以释放病毒、窃取你的账号密码,引发安全性问题。
至于页面脚本,它可以通过浏览器的漏洞来获取系统权限,这些脚本获取系统权限之后也可以对你的电脑做一些恶意的事情,同样也会引发安全问题
在2008年由于Chrome的横空出世,chrome采用多进程架构,比较好的体验快速的占领了使用市场。最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。
架构示意图如下图所示:
基于上面的架构示意图我们来分析多进程架构如何解决单进程的架构的问题
由于进程是相互隔离的,所以当一个页面或者插件崩溃时,影响到的仅仅是当前的页面进程或者插件进程,并不会影响到浏览器和其他页面,这就完美地解决了页面或者插件的崩溃会导致整个浏览器崩溃,也就是不稳定的问题
JavaScript也是运行在渲染进程中的,所以即使 JavaScript 阻塞了渲染进程,影响到的也只是当前的渲染页面,而并不会影响浏览器和其他页面,因为其他页面的脚本是运行在它们自己的渲染进程中的。所以当我们再在 Chrome 中运行上面那个死循环的脚本时,没有响应的仅仅是当前的页面。
对于内存泄漏的解决方法那就更简单了,因为当关闭一个页面时,整个渲染进程也会被关闭,之后该进程所占用的内存都会被系统回收,这样就轻松解决了浏览器页面的内存泄漏问题。
采用多进程架构的额外好处是可以使用安全沙箱,你可以把沙箱看成是操作系统给进程上了一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取任何数据,例如你的文档和桌面
因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。
浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了
在 2016 年,Chrome 官方团队使用“面向服务的架构”(Services Oriented Architecture,简称 SOA)的思想设计了新的 Chrome 架构。也就是说 Chrome 整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过 IPC 来通信,从而构建一个更内聚、松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。
同时 Chrome 还提供灵活的弹性架构,在强大性能设备上会以多进程的方式运行基础服务,但是如果在资源受限的设备上(如下图),Chrome 会将很多服务整合到一个进程中,从而节省内存占用。服务可以弹性部署到不同进程中。核心解决占用资源高的问题,和解耦进程之间的耦合。
安全沙箱:将渲染进程和操作系统隔离的这道墙就是我们要聊的安全沙箱。具体的架构示意图如下图所示
因为网络资源的内容存在着各种可能性,所以浏览器会默认所有的网络资源都是不可信的,都是不安全的。但谁也不能保证浏览器不存在漏洞,只要出现漏洞,黑客就可以通过网络内容对用户发起攻击。但是我们没有给网络进程
设置安全沙箱
, 下载了一个恶意程序,但是没有执行它,那么恶意程序是不会生效的, 但是却给渲染进程
设置了安全沙箱
,由于渲染进程需要执行 DOM 解析、CSS 解析、网络图片解码等操作,如果渲染进程中存在系统级别的漏洞,那么以上操作就有可能让恶意的站点获取到渲染进程的控制权限,进而又获取操作系统的控制权限,这对于用户来说是非常危险的。
浏览器整体发展线路
应用程序 Web化:
随着云计算的普及和 HTML5 技术的快速发展,越来越多的应用转向了浏览器 / 服务器(B/S)架构,这种改变让浏览器的重要性与日俱增,视频、音频、游戏几大核心场景也都在往 Web 的使用场景切换。
Web 应用移动化。
对于移动设备应用,Web 天生具有开放的基因,虽然在技术层面还有问题尚待解决(比如,渲染流程过于复杂且性能不及原生应用、离线时用户无法使用、无法接收消息推送、移动端没有一级入口),但 Google 推出了 PWA 方案来整合 Web 和本地程序各自的优势。顺便说一句,PWA 也是我个人非常期待的方案。
Web 操作系统化。
在我看来,Web 操作系统有两层含义:
纯粹的操作系统
,如ChromeOS
;WebAssembly
;简化渲染流程,使得渲染过程更加直接高效;加大对系统设备特性的支持;提供对复杂Web
项目开发的支持。也就是说,浏览器已经逐步演化成了操作系统之上的“操作系统最后总结推进浏览器架构演进是由于web的世界越来越复杂化。