Q:当我们在浏览器地址栏中输入url到页面展现的过程中都发生了什么?
我们先来了解一下浏览器的多进程架构。
浏览器的多进程架构(以Chrome为例)
进程process与线程thread
多线程可以并行处理任务,但是线程是不能单独存在的,它是由进程来启动和管理的。进程就像是一个有边界的生产厂间,而线程就像是厂间内的一个个员工,可以自己做自己的事情,也可以相互配合做同一件事情。
启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。程序还会创建多个线程来辅助工作,这些线程可以共享这部分内存中的数据。如果应用关闭,进程会被终结,操作系统会释放相关内存。
有时候为了满足功能的需要,创建的进程会叫系统创建另外一些进程去处理其它任务,不过新建的进程会拥有全新的独立的内存空间而不是和原来的进程共用内存空间。这时候进程之间的通信就需要借助IPC(Inter Process Communication)。一个程序采用多进程的优点就是,如果一个工作进程反应迟钝,重启这个进程不会影响应用其它进程的工作,并不会卡死整个程序。
单进程和多进程浏览器(Chrome使用的是多进程)
Chrome 采用多进程架构,其顶层存在一个 Browser process(主进程) 用以协调浏览器的其它进程。
- 浏览器进程:负责包括地址栏,书签栏,前进后退按钮等部分的工作;处理浏览器的一些不可见的底层操作,比如网络请求和文件访问;
- 渲染进程:负责tab内和网页展示相关的所有工作。
- GPU进程:负责独立于其它进程的GPU任务。它之所以被独立为一个进程是因为它要处理来自于不同tab的渲染请求并把它在同一个界面上画出来。
- 插件进程:控制网页使用的所有插件,例如flash插件。
- 扩展进程
- 工具进程
介绍完了浏览器的基本架构模式,接下来我们看看一个常见的导航过程对浏览器来说究竟发生了什么。
浏览器 Tab 外的工作主要由 Browser Process 掌控,Browser Process 又对这些工作进一步划分,使用不同线程进行处理:
- UI thread: 控制浏览器上的按钮及输入框;
- network thread: 处理网络请求,从网上获取数据;
- storage thread: 控制文件等的访问;
回到我们的问题,当我们在浏览器地址栏中输入文字,并点击回车获得页面内容的过程在浏览器看来可以分为以下几步
-
处理输入
UI thread 判断输入是query还是URL
-
开始导航
UI thread 通知 network thread 获取网页内容,并控制 tab 上的 spinner 展现,表示正在加载中。network thread 进行DNS域名解析,随后请求建立TCP连接
-
读取响应
当请求响应返回的时候,network thread 会依据 Content-Type 及 MIME Type sniffing 判断响应内容的格式。 如果响应内容的格式是 HTML ,下一步将会把这些数据传递给 renderer process,如果是 zip 文件或者其它文件,会把相关数据传输给下载管理器。
Safe Browsing 检查也会在此时触发,如果域名或者请求内容匹配到已知的恶意站点,network thread 会展示一个警告页。此外 CORB 检测也会触发确保敏感数据不会被传递给渲染进程。
-
查找渲染进程
当上述所有检查完成,network thread 确信浏览器可以导航到请求网页,network thread 会通知 UI thread 数据已经准备好,UI thread 会查找到一个 renderer process 进行网页的渲染。
-
确定导航
进过了上述过程,数据以及渲染进程都可用了, Browser Process 会给 renderer process 发送 IPC 消息来确认导航,一旦 Browser Process 收到 renderer process 的渲染确认消息,导航过程结束,页面加载过程开始。
此时,地址栏会更新,展示出新页面的网页信息。history tab 会更新,可通过返回键返回导航来的页面,为了让关闭 tab 或者窗口后便于恢复,这些信息会存放在硬盘中。
-
额外步骤
一旦导航被确认,renderer process 会使用相关的资源渲染页面,当 renderer process 渲染结束(渲染结束意味着该页面内的所有的页面,包括所有 iframe 都触发了 onload 时),会发送 IPC 信号到 Browser process, UI thread 会停止展示 tab 中的 spinner。