浏览器原理系列 --- 宏观篇

1,063 阅读13分钟

概述

基于一个高频面试题‘浏览器地址栏输入到展示发生了什么’引发的深入思考;如果要很好的回答这个问题,需要比较全面地掌握计算机很多知识,其中涉及到了网络、操作系统、Web等一系列的知识。这次通过浏览器底层原理系列分析来更好地回答这个问题。

主要通过以下四个系列学习浏览器原理:

  • 浏览器宏观框架
  • 浏览器网络原理
  • 浏览器渲染引擎
  • 浏览器安全

收敛发现问题本质

针对问题‘浏览器地址栏输入到展示发生了什么’可能我们很多人的脑海中第一印象是如下图所示答案比较发散:

实际如果有关注过Chrome浏览器的任务管理系统,我们可以发现浏览器打开一个标签页面会启动四个进程

基于这四个进程我们可以重新梳理发散的答案,对这些答案进行收敛如下图所示

通过简单收敛我们可以发现学习浏览器原理只要学习这四类进程之间在做什么进行归类学习。

学习之前可以追溯历史看看浏览器的发展历史,方便我们在后续学习中针对遇到的一些可以找到历史的影子。

进程 VS 线程

进程

进程可以被描述为一个应用的执行程序,线程存在于进程并执行任意部分。操作系统为进程提供了一块可以使用的内存[数据],应用的所有状态都保存在该私有内存空间中,关闭应用,进程会关闭,操作系统释放内存

进程如下图所示:

如图所示单独的进程中包含了可执行代码、内存数据、操作系统文件等

进程具有的特征:

  • 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
  • 并发性:任何进程都可以同其他进行一起并发执行;
  • 独立性:进程是系统进行资源分配和调度的一个独立单位;
  • 结构性:进程由程序,数据和进程控制块三部分组成

线程

  早期的操作系统中并没有线程的概念,后来随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。   

  • 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  • 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
  • 同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;
  • 调度和切换:线程上下文切换比进程上下文切换要快得多

多线程

从上图可以看出,线程 1、线程 2、线程 3 分别把执行的结果写入 A、B、C 中,然后线程 2 继续从 A、B、C 中读取数据,用来显示执行结果

总结

  • 进程之间的内容相互隔离
  • 当一个进程关闭之后,操作系统会回收进程所占用的内存
  • 线程之间共享进程中的数据
  • 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
  • 线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率
  • 线程是不能单独存在的,它是由进程来启动和管理的

浏览器架构演进

单进程浏览器架构

单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。在2007年之前的浏览器都基本是单进程浏览器。

可以看出只有一个进程(progress), 多个功能模块都是通过多个线程并行处理任务实现。

单进程架构问题

问题1:不稳定

早起浏览器都是通过第三方插件来实现诸如 Web 视频、Web 游戏等各种强大的功能,但是插件是最容易出问题的模块。这些插件以线程运行在浏览器,一个插件的意外崩溃会引起整个浏览器的崩溃。

渲染引擎模块也是不稳定的。通常一些复杂的 JavaScript 代码就有可能引起渲染引擎模块的崩溃。

问题2:不流畅

从上面的“单进程浏览器架构示意图”可以看出,所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的,这就意味着同一时刻只能有一个模块可以执行

function freeze() {
    while (1) { 
        console.log("freeze");
    }
};
freeze();

因为这个脚本是无限循环的,所以当其执行时,它会独占整个线程,这样导致其他运行在该线程中的模块就没有机会被执行。因为浏览器中所有的页面都运行在该线程中,所以这些页面都没有机会去执行任务,这样就会导致整个浏览器失去响应,变卡顿。

页面的内存泄漏也是单进程变慢的一个重要原因。通常浏览器的内核都是非常复杂的,运行一个复杂点的页面再关闭页面,会存在内存不能完全回收的情况,这样导致的问题是使用时间越长,内存占用越高,浏览器会变得越慢。

问题3:不安全

插件可以使用 C/C++ 等代码编写,通过插件可以获取到操作系统的任意资源,当你在页面运行一个插件时也就意味着这个插件能完全操作你的电脑。如果是个恶意插件,那么它就可以释放病毒、窃取你的账号密码,引发安全性问题。

至于页面脚本,它可以通过浏览器的漏洞来获取系统权限,这些脚本获取系统权限之后也可以对你的电脑做一些恶意的事情,同样也会引发安全问题

多进程浏览器架构

在2008年由于Chrome的横空出世,chrome采用多进程架构,比较好的体验快速的占领了使用市场。最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。

架构示意图如下图所示:

基于上面的架构示意图我们来分析多进程架构如何解决单进程的架构的问题

如何解决单进程问题

解决不稳定的问题

由于进程是相互隔离的,所以当一个页面或者插件崩溃时,影响到的仅仅是当前的页面进程或者插件进程,并不会影响到浏览器和其他页面,这就完美地解决了页面或者插件的崩溃会导致整个浏览器崩溃,也就是不稳定的问题

解决不流畅的问题

JavaScript也是运行在渲染进程中的,所以即使 JavaScript 阻塞了渲染进程,影响到的也只是当前的渲染页面,而并不会影响浏览器和其他页面,因为其他页面的脚本是运行在它们自己的渲染进程中的。所以当我们再在 Chrome 中运行上面那个死循环的脚本时,没有响应的仅仅是当前的页面。

对于内存泄漏的解决方法那就更简单了,因为当关闭一个页面时,整个渲染进程也会被关闭,之后该进程所占用的内存都会被系统回收,这样就轻松解决了浏览器页面的内存泄漏问题。

解决安全问题

采用多进程架构的额外好处是可以使用安全沙箱,你可以把沙箱看成是操作系统给进程上了一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取任何数据,例如你的文档和桌面

各个进程职责

  • 浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  • 渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  • GPU 进程。其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
  • 网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

多进程架构问题

问题1 比较高的资源占用

因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。

问题2 复杂的体系架构

浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了

面向服务的架构(SOP)

在 2016 年,Chrome 官方团队使用“面向服务的架构”(Services Oriented Architecture,简称 SOA)的思想设计了新的 Chrome 架构。也就是说 Chrome 整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过 IPC 来通信,从而构建一个更内聚、松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。

同时 Chrome 还提供灵活的弹性架构,在强大性能设备上会以多进程的方式运行基础服务,但是如果在资源受限的设备上(如下图),Chrome 会将很多服务整合到一个进程中,从而节省内存占用。服务可以弹性部署到不同进程中。核心解决占用资源高的问题,和解耦进程之间的耦合。

安全沙箱

安全沙箱:将渲染进程和操作系统隔离的这道墙就是我们要聊的安全沙箱。具体的架构示意图如下图所示

因为网络资源的内容存在着各种可能性,所以浏览器会默认所有的网络资源都是不可信的,都是不安全的。但谁也不能保证浏览器不存在漏洞,只要出现漏洞,黑客就可以通过网络内容对用户发起攻击。但是我们没有给网络进程设置安全沙箱, 下载了一个恶意程序,但是没有执行它,那么恶意程序是不会生效的, 但是却给渲染进程设置了安全沙箱,由于渲染进程需要执行 DOM 解析、CSS 解析、网络图片解码等操作,如果渲染进程中存在系统级别的漏洞,那么以上操作就有可能让恶意的站点获取到渲染进程的控制权限,进而又获取操作系统的控制权限,这对于用户来说是非常危险的。

安全沙箱好处

  • 安全沙箱设置的最小单位是进程。单进程设置会导致功能不可用
  • 站点隔离,同一个标签页不同进程,互相不影响;不会导致恶意程序

安全沙箱不便利

  • 持久存储受影响 -- 例如文件存储
  • 用户交互事件 --- 必须通过主进程
  • 网络访问 ---- 同源策略

总结

浏览器整体发展线路

  1. 应用程序 Web化:

    随着云计算的普及和 HTML5 技术的快速发展,越来越多的应用转向了浏览器 / 服务器(B/S)架构,这种改变让浏览器的重要性与日俱增,视频、音频、游戏几大核心场景也都在往 Web 的使用场景切换。

  2. Web 应用移动化。

    对于移动设备应用,Web 天生具有开放的基因,虽然在技术层面还有问题尚待解决(比如,渲染流程过于复杂且性能不及原生应用、离线时用户无法使用、无法接收消息推送、移动端没有一级入口),但 Google 推出了 PWA 方案来整合 Web 和本地程序各自的优势。顺便说一句,PWA 也是我个人非常期待的方案。

  3. Web 操作系统化。

    在我看来,Web 操作系统有两层含义:

    • 利用 Web 技术构建一个纯粹的操作系统,如ChromeOS
    • 是浏览器的底层结构往操作系统架构方向发展,在整个架构演化的大背景下会牵涉诸多改变。Chrome 朝着 SOA 的方向演化,未来很多模块都会以服务的形式提供给上层应用使用;在浏览器中引入多种编程语言的支持,比如新支持的 WebAssembly;简化渲染流程,使得渲染过程更加直接高效;加大对系统设备特性的支持;提供对复杂Web 项目开发的支持。也就是说,浏览器已经逐步演化成了操作系统之上的“操作系统

最后总结推进浏览器架构演进是由于web的世界越来越复杂化。