[toc]
一、浏览器的常用进程
1.进程和线程的区别
线程可以共享地址空间和数据.而进程独享地址空间和数据.
线程比进程更轻量.所以他的切换和创建销毁更快速.
多个线程可以共享公共内存.因此更容易进行通信
线程必须在进程中运行,但是进程和线程是不同的东西,线程拥有自己的程序计数器,寄存器,和一个堆栈.
进程是资源的聚合体.而线程则是CPU上被调度的实体.线程可以访问进程地址空间中的每一个内存地址,甚至可以访问其他线程的堆栈,因此线程之间是没有保护的.
进程中的任意一线程执行出错,都会导致整个进程的崩溃。
线程之间共享进程中的数据。
当一个进程关闭之后,操作系统会回收进程所占用的内存。
进程之间的内容相互隔离。
2.多进程浏览器
以chrome举例
- 浏览器进程:页面显示、用户交互、子进程管理,同时提供存储等功能
- 渲染进程:解析HTML、CSS、js为用户可交互的网页,排版引起blink 和JavaScript引擎V8运行在进程下,默认情况每个网页Tab都会创建一个渲染进程(同一个根域名下的网站会公用同一个渲染进程)。渲染进程运行在沙箱模式下(独立作业环境,内部操作不会影响外部系统,只能访问自己内部目录和资源,并且与其它进程独立)
- GPU进程:用了实现3D CSS效果,video播放,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。
- 网络进程。主要负责页面的网络资源加载。
- 插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
二、浏览器发起http请求流程
1.构建请求
如GET /index.html HTTP1.1,构建好后,浏览器准备发起网络请求
2.查找缓存
先查找浏览器中是否有缓存,有的话拦截请求,返回缓存。如果没有,就进入网络请求
3.准备ip地址和端口
通过DNS服务获取网站对应的IP地址,同时host也有缓存:根域名服务器->顶级域名服务器->网络服务提供商缓存->路由器缓存->系统缓存->浏览器缓存
端口号没有指定的话,默认是80
4.等待TCP队列
Chrome有个机制,同一个域名同时最多只能建立6个TCP连接,如果在同一个域名下同时有10个请求发生,那么其中4个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于6,会直接进入下一步,建立TCP连接
5.建立TCP链接
包括三次握手,如果使用HTTPS,还需要TLS层的四次加密协商。七次握手
6.发送HTTP请求
首先浏览器会向服务器发送请求行,它包括了请求方法、请求URI(Uniform Resource Identifier)和HTTP版本协议。然后再发送请求头(包含浏览器的一些基础信息)和请求体(GET请求不需要)
7.处理http响应
这里 包括处理响应和状态码,断开链接,重定向(如果状态码是301)三个步骤
如果响应状态码是301,则会发送重定向,需要再次请求,并读取响应头中的重定向地址作为第二次发请求的地址。
浏览器的缓存过程
DNS缓存和页面资源缓存会被缓存在浏览器中,而页面资源则是由几个请求头中的字段决定
包括Cache-Control:Max-age = 2000 表示缓存过期时间2000秒
If-None-Match:"4f80f-13c-3a1xb12a" 增加文件的etag,供后天判断资源是否是最新
请求流程总结
三、从输入URL到页面展示的过程
主要职责划分
- 浏览器进程主要负责用户交互、子进程管理和文件储存等功能
- 网络进程是面向渲染进程和浏览器进程等提供网络下载功能
- 渲染进程的主要职责是把从网络下载的HTML、JavaScript、CSS、图片等资源解析为可以显示和交互的页面。因为渲染进程所有的内容都是通过网络获取的,会存在一些恶意代码利用浏览器漏洞对系统进行攻击,所以运行在渲染进程里面的代码是不被信任的,运行在沙箱中
1. 用户输入
地址栏判断输入的是搜索内容还是URL,如果是内容,会结合默认搜索引擎来组成URL,如果是URL,会加上协议(https\http)形成完整的URL
敲入回车后,浏览器给当前页面发送一个beforeunload的事件,beforeunload事件允许页面在退出之前执行一些数据清理操作,还可以询问用户是否要离开当前页面,接着进入加载状态,如下图,此时页面内容没有变化。
2.URL请求过程
浏览器进程通过IPC机制把URL请求发送到网络进程,网络进程先判断本地缓存,如果有直接返回缓存,然后进行DNS解析,建立连接,发起网络请求。等待请求回来的后续处理
2.1重定向
如果响应头是301或302,需要重定向到其它URL。网络进程会从响应头的Location字段里面读取重定向的地址,然后再发起新的HTTP或者HTTPS请求,一切又重头开始了。
2.2 响应数据类型
Content-Type是HTTP头中一个非常重要的字段, 它告诉浏览器服务器返回的响应体数据是什么类型,不同Content-Type的后续处理流程也不同,如果是HTML,那么浏览器则会继续进行导航流程。由于Chrome的页面渲染是运行在渲染进程中的,所以接下来就需要准备渲染进程。
2.3准备渲染进程
打开一个新页面采用的渲染进程策略就是:
- 通常情况下,打开新的页面都会使用单独的渲染进程;
- 如果从A页面打开B页面,且A和B都属于同一站点的话,那么B页面复用A页面的渲染进程;如果是其他情况,浏览器进程则会为B创建一个新的渲染进程。
- 每个跨站点 iframe 运行在单独的渲染器进程
2.4 提交文档
网络请求由网络进程发起和接受,但是数据要由渲染进程处理,这里需要借助浏览器进程的控制,使网络进程和渲染进程进行通信。提交文档就是浏览器进程将网络进程接收到的HTML数据提交给渲染进程,如下:
- 首先当浏览器进程接收到网络进程的响应头数据之后,便向渲染进程发起“提交文档”的消息;
- 渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道”(IPC);
- 等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程;
- 浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的URL、前进后退的历史状态,并更新Web页面(白屏页面)
网站通常使用图像、CSS 和 JavaScript 等外部资源。这些文件需要从网络或缓存中加载。主线程可以在解析构建DOM的过程中找到它们时一一请求,但为了加快速度,“预加载扫描器”是并发运行的。如果有喜欢的东西<img>
或<link>
在通过HTML解析器生成的标记在HTML文档中,预紧扫描器扫描并发送请求到在浏览器过程中的网线。而当解析到<seript>标签时,则会暂停HTML文档的解析,并且必须加载、解析和执行 JavaScript 代码,因为JavaScript可以通过document.write()改变整个 DOM 结构之类的东西来改变文档的形状。
如果您的 JavaScript 不使用document.write()
,您可以添加async
或defer
属性到<script>
标记,浏览器异步加载和运行 JavaScript 代码,并且不会阻止解析。
2.5渲染阶段
一旦文档被提交,渲染进程便开始页面解析和子资源加载,等待渲染完成后,渲染进程发消息给浏览器进程,浏览器进程完成加载动画,展示新的页面。
3.渲染流程
渲染流程就是把HTML、CSS、javas经过转化,转为页面的过程。
一个渲染流程会划分很多子阶段,整个处理过程叫渲染流水线,流水线可分为如下几个子阶段:构建DOM树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。每个阶段都经过输入内容 -->处理过程-->输出内容三个部分。
3.1构建DOM树
这是因为浏览器无法直接理解和使用HTML,所以需要将HTML转换为浏览器能够理解的结构——DOM树
每个DOM树的一个节点就是HTML中的一个标签,这种转化其实很常见。DOM是保存在内存中树状结构,可以通过JavaScript来查询或修改其内容。
3.2样式计算(Recalculate Style)
样式计算是为了计算Dom节点的每个元素的具体样式,这里就需要CSS的参与。分为三个阶段:
3.2.1 CSS转化为styleSheets
CSS样式来源主要有三种:
- 通过link引用的外部CSS文件
- style标记内的 CSS
- 元素的style属性内嵌的CSS
当渲染引擎接收到CSS文本时,会执行一个转换操作,将CSS文本转换为浏览器可以理解的结构——StyleSheets。这是一种结构体,渲染引擎会把所有获取到的CSS转换为styleSheets结构中的数据,并且具备了查询和修改功能,这其实是CSSDOM。
3.2.2属性值标准化
就是把CSS中的属性值,如2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
- initial value:初始值,我们手动指定的值
- specified value:从 css、父节点、初始值来的默认值
- resolved value:给 CSSOM 用的值
- computed value:inheritance 结束后,计算出一些和布局无关的相对值,从specified value计算得来
- used value:布局计算后,确定的值
- actual value:根据运行环境的限制,然后近似计算后得到 actual value
3.2.3 计算DOM元素的样式
就是把CSSDom 的数据绑定到DOM树上,通过Css的继承规则和层叠规则。CSS继承就是每个DOM节点都包含有父节点的样式,也就是所有子节点都继承了父节点样式,Chrome中的效果:
第二个规则是样式层叠,表示如何合并来自多个CSS源的属性值的算法。这两步最终的结果是计算每个DOM元素的具体样式,并保存在ComputedStyle的结构内。
3.3布局阶段
我们有了DOM树和DOM树种元素的样式,接着需要计算DOM树中可见元素的几何位置,整个过程叫布局。
3.3.1创建布局树
有些元素可能不会展示出来,所有创建一棵只包含可见元素的布局树,(display:none,head标签)不再该树中,但是visibility: hidden
在布局树中。
3.3.2计算布局
上一步的布局树需要递归的计算布局,因为一个节点大小的计算通常需要先计算它的子节点的位置、大小信息等。对于布局树中的每个节点我们称为RenderObject,因为这些节点是最终需要被渲染出来的节点。
- 首先会判断RenderObject节点是否需要重新计算,通过检查相应的标记位、子女是否需要计算布局等来确定
- 其次判断网页的宽度和垂直方向的外边距,通常网页在垂直方向移动,水平方向尽量步滚动
- 然后遍历每个子女节点,计算他们的布局,每个元素实现layout函数来计算该元素的布局,如果自己 有宽高,就使用自己的设定,否则根据内容填充
- 节点根据子女节点的大小计算自己的高度,整个过程结束
如果有样式发生变化,可视区域发生变化,产生动画,JavaScript修改样式信息,用户交互时就需要重新布局。
3.4分层
渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。正是这些图层叠加在一起构成了最终的页面图像。
layoutTree就是上边布局后形成的树,然后又会产生图层树,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
那些树会有单独的一层?
- 拥有层叠上下文属性的元素会被提升为单独的一层: 层叠上下文介绍
- 需要剪裁(clip)的地方也会被创建为图层,剪裁就是节点内容超过节点限制
- 如果出现滚动条,滚动条也会被提升为单独的层
3.5图层绘制(tiles)
渲染引擎会把图层树中的每个图层进行分别绘制,并且把一个图层的绘制拆分成很多小的绘制指令,形成待绘制列表
从图中可以看出,绘制列表中的指令就是让其执行一个绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表
3.6栅格化(raster)操作
主要操作为:
- 提交到合成线程
- 合成线程把图层分块
- 转到栅格化线程来把图块转为位图
- 栅格化线程通过IPC借助gpu线程完成栅格化
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。你可以结合下图来看下渲染主线程和合成线程之间的关系:
- 当渲染引擎中图层的绘制列表准备好之后,主线程会把该列表提交(commit)给合成线程。因为有时候图层的大小超过屏幕上的可见区域(视口Viewport),全部绘制会浪费资源,合成线程会把图层进行划分为图块(tile),通常图块的大小通常是256x256或者512x512。
- 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的,也就是由合成线程转化到了栅格化线程。(此时还在渲染进程中)
- 栅格化过程都会使用GPU来加速生成,使用GPU生成位图的过程叫快速栅格化,生成的位图被保存在GPU内存中。GPU操作是运行在GPU进程,最终生成位图的操作是在GPU中完成的,这就涉及到了跨进程操作。
3.7合成和显示 (draw quad)
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。浏览器进程会处理该命令,把页面绘制到内存中,最后将内存显示在屏幕上。
一个完整的渲染流程大致可总结为如下:
- 渲染进程将HTML内容转换为能够读懂的DOM树结构。
- 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets(生存CSSDOM树),计算出DOM节点的样式。
- 创建布局树(LayoutTree),并计算元素的布局信息。
- 对布局树进行分层,并生成分层树(LayerTree)。
- 为每个图层生成绘制列表,并将其提交到合成线程。
- 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
- 合成线程发送绘制图块命令DrawQuad给浏览器进程。
- 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上。
3.9相关概念
重排
通过JavaScript或者CSS修改元素的几何位置属性如宽高,会触发重新布局等一系列子阶段,这个过程叫重排。重排需要更新完整的渲染流水线,开销最大
重绘
更改元素的背景颜色,不会触发布局解决,直接进入绘制阶段等子阶段,叫重绘。重绘比重排少了布局和合成阶段,效率高一些
直接合成
如果你更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成,如CSS的transform来实现动画效果,在非主线程进行合成动画,合成的效率比前两者高很多。
4.0输入事件
事件接收
当用户在屏幕上发生触摸等手势时,浏览器进程首先接收到该手势。但是,浏览器进程只知道该手势发生的位置,因为选项卡内的内容由渲染器进程处理。因此浏览器进程将事件类型(如touchstart
)及其坐标发送到渲染器进程。渲染器进程通过查找事件目标并运行附加的事件侦听器来适当地处理事件。
传递给主线程
由于运行 JavaScript 是主线程的工作,因此当页面被合成时,合成器线程将页面中附加了事件处理程序的区域标记为“非快速滚动区域”。通过拥有这些信息,如果事件发生在该区域,合成器线程可以确保将输入事件发送到主线程。如果输入事件来自该区域之外,则合成器线程继续合成新帧,而无需等待主线程。