⑤浏览器的工作原理(上)

1 浏览器的工作原理

1.1 多进程的浏览器(原地址)

1.1.1 进程和线程的区别

进程是CPU资源分配的最小单位(是能拥有资源和独立运行的最小单位);
不同进程之间也可以通信,不过代价较大;
线程是CPU调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程);
现在,一般通用的叫法:单线程与多线程,都是指在一个进程内的单和多;
同一进程下的各个线程之间共享进程的内存空间(包括代码段、数据集、堆等)。

1.1.2 浏览器的多进程

Browser进程:浏览器的主进程(负责协调、主控),只有一个。作用:
  <1> 负责浏览器界面显示,与用户交互。如前进,后退等;
  <2> 负责各个页面的管理,创建和销毁其他进程;
  <3> 将Renderer进程得到的内存中的Bitmap(位图),绘制到用户界面上;
  <4> 网络资源的管理,下载等。
第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建;
GPU进程:最多一个,用于一些图形操作、3D绘制等;
Renderer进程(浏览器内核,内部是多线程的):默认每个Tab页面一个进程,互不影响。主要作用为页面渲染,脚本执行,事件处理等。

1.1.3 Renderer进程的多线程

 浏览器最重要的部分就是Renderer进程,也叫做浏览器内核,一般的浏览器内核会包括以下一些常驻线程:
GUI渲染线程:
  <1> 负责渲染浏览器界面,解析HTML,CSS,构建DOM树,布局和绘制等;
  <2> 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行;
  <3> 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎线程执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎线程空闲时立即被执行。

JS引擎线程:
  <1> 也称为JS内核,负责处理Javascript脚本程序,在第④章我们已经知道JS引擎是单线程的;
  <2> JS引擎线程负责解析Javascript脚本,运行代码;
  <3> JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序;
  <4> 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

事件触发线程:
  <1> 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解为,JS引擎自己都忙不过来,需要浏览器另开线程协助);
  <2> 接受浏览器里面的操作事件响应,如在监听到鼠标、键盘、AJAX异步请求等事件的时候,会将对应任务添加到事件线程中;
  <3> 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;
  <4> 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)。

定时触发器线程:
  <1> 传说中的setInterval()(在参数指定的时间后将待执行方法放到执行队列中, 如果队列中没有其他方法等待,则会立即执行setTimeout()指定的方法)与setTimeout()(定时触发器线程每间隔指定的时间将指定方法放入到执行队列中, 当函数执行时,如果发现同一个定时器已经有多个在等待执行的任务,只会执行1次。后面的会被忽略掉)所使用线程;
  <2> 浏览器定时计数器并不是由JavaScript引擎计数的(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确);
  <3> 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行);
  <4> 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

异步http请求线程:
 XMLHttpReques在连接后,发送请求是由浏览器新开一个线程请求,当请求的状态变更时,如果先前已设置回调,这异步线程就产生状态变更事件放到JavaScript引擎的处理队列中等待处理,当任务被处理时,JavaScript引擎始终是单线程运行回调函数,具体点即还是单线程运行onreadystatechange事件里面所设置好的函数。

注意:

 不同的浏览器内核对网页的解析过程不同,因此同一网页在不同的内核的浏览器里的渲染(显示)效果也可能不同,现今的四大内核:WebKitBlinkTridentGecko;Chrome浏览器现在使用的就是Blink内核,严格来说应该是Blink内核再内嵌一个V8的Javascript引擎。

Blink内核

1.1.4 Renderer进程和Browser进程、GPU进程的通信过程

Browser进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程;
Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程;
GUI渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
当然可能会有JS线程操作DOM(这样可能会造成回流并重绘)
最后Render进程将结果传递给Browser进程去呈现;

1.1.5 解决JS阻塞页面加载

 因为GUI渲染线程与JS引擎线程是互斥的,所以JS引擎如果执行时间过长就会阻塞页面,解决方法:使用HTML5提供的专用线程Web Workers,当创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM),然后JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)。

1.1.6 WebWorker与SharedWorker的区别

HTML5还有个新特性Shared Worker,叫做共享线程,多个页面可以共用一个SharedWorker后台线程,并且可通过该后台线程共享数据,但必须保证这些页面都是同源的(相同的协议,主机和端口号);
创建SharedWorker线程的方法与前面创建Worker线程的方法类似,只是构造器略有区别,代码如下:

var worker=new SharedWorker(url, [name]);

该方法第一个参数用于指定后台线程文件的URL地址,该脚本文件中定义了在后台线程中所要执行的处理,第二个参数为可选参数,用于指定Worker的名称,当用户创建多个SharedWorker对象时,脚本程序将根据创建SharedWorker对象时使用的url参数值与name参数值来决定是否创建不同的线程;
Chrome浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中每个相同的JavaScript只存在一个管理SharedWorker的进程,不管它被创建多少次;
SharedWorker和WebWorker的区别在于:SharedWorker由独立的进程管理,WebWorker只是属于render进程下的一个线程。

1.2 浏览器运行流程

1 域名解析;
2 发起TCP三次握手建立连接;
3 使用HTTP协议或者HTTPS协议向服务端请求页面;
4 把请求回来的HTML代码经过渲染引擎解析,先构成一棵DOM树;
5 流式计算DOM树上的CSS属性,得到一棵包含样式信息的DOM树;
6 流式计算每个元素的位置和大小;
7 最后根据这些样式信息和大小信息,为每个元素在内存中渲染它的图形,并且把它绘制到界面上。

1.2.1 域名解析

浏览器会首先检查自身缓存,浏览器会缓存DNS记录一段时间;
如果浏览器缓存里没有找到需要的记录,浏览器会做一个操作系统调用(windows里是get host name),查询系统DNS缓存中的域名表,有则直接使用;
系统缓存中还是没有,则检查hosts文件中的映射表;
本地实在找不到,则向DNS域名服务器发起请求查询;
  1 主机先向其本地域名服务器(每一个ISP(网络服务提供商)都会有一个自己的本地域名服务器)进行递归查询,如果缓存中没有,继续下一步;
  2 本地域名服务器则发起迭代DNS请求,首先向根域服务器发起请求查询;
  3 假如本次请求的是www.baidu.com,根域服务器发现这是一个com的顶级域名,就把顶级域名服务器dns.com地址返回给本地域名服务器;
  4 本地域名服务器再向顶级域名服务器dns.com地址请求www.baidu.com,此时本地服务器返回了权限域名服务器dns.baidu.com的IP地址;
  5 本地域名服务器向权限域名服务器dns.baidu.com进行查询请求;
  6 权限域名服务器dns.baidu.com告诉本地域名服务器,所查询的主机的IP地址,然后本地域名服务器再把查询结果告诉操作系统内核,内核再返回给浏览器;
如果连DNS服务器也没解析成功,操作系统就会查找NetBIOS名称缓存(如果这台计算机曾经与对方通信过,则对方计算机的NetBIOS名称和IP地址会被存储到这台计算机的NetBIOS名称缓存中);
如果第⑤步也没有成功,那会查询WINS服务器(是NETBIOS名称和IP地址对应的服务器);
如果第⑥步也没有查询成功,那么操作系统就要进行广播查找;
如果第⑦步也没有成功,那么操作系统就读取LMHOSTS文件(和HOSTS文件同一个目录下,写法也一样),如果这一步还没有解析成功,那么就宣告这次解析失败,那就无法跟目标计算机进行通信;只要这八步中有一步可以解析成功,那就可以成功和目标计算机进行通信。

1.2.2 TCP三次握手

三次握手

第一次握手:客户端向服务器发送SYN报文,并发送客户端初始序列号Seq=X;等待服务器确认;
第二次握手:服务器接收客户端的SYN报文,然后向客户端返回一个包SYN+ACK响应报文,并发送初始序列号Seq=Y;
第三次握手:客户端接受SYN+ACK报文,并向服务器发送一个ACK确认报文,至此连接建立。

1.2.3 使用HTTP协议或者HTTPS协议向服务端请求页面 ★

建立了TCP连接后,浏览器就会向服务器发送请求命令,例如:

GET/ HTTP/1.1
<!-- 请求方法/请求的路径+协议/版本 -->
<!--
除了请求行,一般还会带有请求头(Requset Header),会包含许多有关的客户端环境和请求正文的有用信息,
例如,请求头可以声明浏览器所用的语言:如Accept-Language:zh-cn,
还有请求正文的长度等
 -->
<!-- 
然后接下来的是请求正文,
请求头和请求正文必须有一个空行,这个行非常重要,它表示请求头已经结束,接下来的是请求正文。请求正文中可以包含客户提交的查询字符串信息:
如username=jinqiao&password=1234
 -->

浏览器向服务器发出请求后,服务器会回送应答:


HTTP/1.1 301 Moved Permanently
<!-- 状态行 -->
<!-- 协议/版本 状态码 状态文本 -->
<!-- 下面是响应头(Response Header)包含许多信息,例如服务器类型、日期时间、内容类型和长度等 -->
Date: Fri, 25 Jan 2019 13:28:12 GMT
Content-Type: text/html
Content-Length: 182
Connection: keep-alive
<!-- 
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码Connection:keep-alive,
TCP连接就会在发送后仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求,
保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽
 -->

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>openresty</center>
</body>
</html>
<!-- 上面是服务器返回的响应正文——HTML代码 -->
HTTP协议

1.2.3.1 请求方法

  • GET
  • POST
  • HEAD
  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE

浏览器通过地址栏访问页面都是GET方法。表单提交产生POST方法;
HEAD则是跟GET类似,只返回请求头,多数由JavaScript发起;
PUT和DELETE分别表示添加资源和删除资源,但是实际上这只是语义上的一种约定,并没有强约束;
CONNECT现在多用于HTTPS和WebSocket;
OPTIONS和TRACE一般用于调试,多数线上服务都不支持。

1.2.3.2 状态码和状态文本

  • 1xx:临时回应,表示客户端请继续(由于HTTP/1.0协议中没有定义任何1xx状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送 1xx 响应);
  • 2xx:请求成功;
    200:请求成功。
  • 3xx:表示请求的目标有变化,希望客户端进一步处理;
    301&302:永久性与临时性跳转;
    304:跟客户端缓存没有更新。(客户端本地已经有缓存的版本,并且在Request中告诉了服务端,当服务端通过时间或者tag,发现没有更新的时候,就会返回一个不含body的304状态)
  • 4xx:客户端请求错误;
    403:服务器拒绝访问,权限不够;
    404:表示请求的页面不存在;
    418:It’s a teapot. 这是一个彩蛋,来自ietf的一个愚人节玩笑。
  • 5xx:服务端请求错误。
    500:服务端错误;
    503:服务端暂时性错误,可以一会再试。

HTTP状态码

1.2.3.3 常见的请求头和响应头

请求头
响应头

1.2.3.4 常见的请求正文格式

  • application/json
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/xml

1.2.3.5 HTTP 2与HTTP 1.1的不同点

  • 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮;
  • 多路复用(MultiPlexing),即则使用同一个TCP连接来传输多个HTTP请求,避免了TCP连接建立时的三次握手开销,和初建TCP连接时传输窗口小的问题;
  • header压缩,HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
  • 服务端推送(server push),服务端推送能够在客户端发送第一个请求到服务端时,提前把一部分内容推送给客户端,放入缓存当中,这可以避免客户端请求顺序带来的并行度不高,从而导致的性能问题。

1.2.3.6 HTTPS与HTTP的一些区别

  • HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费;
  • HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运W行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的;
  • HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443;
  • HTTPS可以有效的防止运营商劫持(什么是运营商劫持),解决了防劫持的一个大问题。

1.2.4 创建DOM树 ★

创建DOM树

 DOM(Document Object Model 文档对象模型)是为HTML和XML提供的API,可以把html、xml文档变成一种具有树形结构的object,我们如果需要修改这个object,可以通过在JS代码中调用DOM API。
 浏览器在接收HTML代码(响应正文)后,将代码交予渲染引擎解析:

调用状态机(HTML官方文档)将每一个字符解析成词(指编译原理的术语token,表示最小的有意义的单元);
用语法分析器接收token,一边接收,一边构建DOM树,用栈进行保存和输出,栈顶是HTML代码(已转成tokens)最后的根节点,stack[0]就是DOM树的产出。

tokens->DOM树的构建过程:

不同的HTML节点对应了不同的Node的子类,解析时将节点转换为Node类的实例;
栈顶元素就是当前节点(正在解析着的token词);
解析时遇到属性,就添加到当前节点中;
解析时遇到文本节点,如果当前节点是文本节点,则跟文本节点合并,否则入栈成为当前节点的子节点;
解析时遇到注释节点,作为当前节点的子节点;
解析时遇到tag start(开始标签)就入栈一个节点,当前节点就是这个节点的父节点;
解析时遇到tag end(结束标签)就出栈一个节点(还可以检查是否匹配)。

 全部出栈后,DOM树构建完毕。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342