欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浏览器相关原理(面试题)详细总结一

程序员文章站 2023-11-15 17:19:52
1. Chrome 打开一个页面需要启动多少进程?分别有哪些进程? 浏览器从关闭状态进行启动,然后新开 1 个页面至少需要 1 个网络进程、1 个浏览器进程、1 个 GPU 进程以及 1 个渲染进程,共 4 个进程;后续再新开标签页,浏览器、网络进程、GPU进程是共享的,不会重新启动,如果2个页面属 ......

1. chrome 打开一个页面需要启动多少进程?分别有哪些进程?

浏览器从关闭状态进行启动,然后新开 1 个页面至少需要 1 个网络进程、1 个浏览器进程、1 个 gpu 进程以及 1 个渲染进程,共 4 个进程;后续再新开标签页,浏览器、网络进程、gpu进程是共享的,不会重新启动,如果2个页面属于同一站点的话,并且从a页面中打开的b页面,那么他们也会共用一个渲染进程,否则新开一个渲染进程。

最新的 chrome 浏览器包括:1 个浏览器(browser)主进程、1 个 gpu 进程、1 个网络(network)进程、多个渲染进程和多个插件进程。

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

2. 如何保证页面文件能被完整送达浏览器?

互联网中的数据是通过数据包来传输的。数据包要在互联网上进行传输,就要符合网际协议(ip),互联网上不同的在线设备都有唯一的地址,地址只是一个数字,只要知道这个具体的地址,就可以往这里发送信息。

如果要想把一个数据包从主机 a 发送给主机 b,那么在传输之前,数据包上会被附加上主机 b 的 ip 地址信息,这样在传输过程中才能正确寻址。额外地,数据包上还会附加上主机 a 本身的 ip 地址,有了这些信息主机 b 才可以回复信息给主机 a。这些附加的信息会被装进一个叫 ip 头的数据结构里。ip 头是 ip 数据包开头的信息,包含 ip 版本、源 ip 地址、目标 ip 地址、生存时间等信息。

ip 是非常底层的协议,只负责把数据包传送到对方电脑,但是对方电脑并不知道把数据包交给哪个程序,是交给浏览器还是交给王者荣耀?因此,需要基于 ip 之上开发能和应用打交道的协议,最常见的是用户数据包协议(user datagram protocol),简称udp传输控制协议(transmission control protocol),简称tcp.

基本传输过程为:

  1. 上层将数据包交给传输层
  2. 传输层会在数据包前面附加上udp 头,组成新的 udp 数据包,再将新的 udp 数据包交给网络层
  3. 网络层再将 ip 头附加到数据包上,组成新的 ip 数据包,并交给底层
  4. 数据包被传输到主机 b 的网络层,在这里主机 b 拆开 ip 头信息,并将拆开来的数据部分交给传输层
  5. 在传输层,数据包中的 udp 头会被拆开,并根据 udp 中所提供的端口号,把数据部分交给上层的应用程序
  6. 最终,数据包就发送到了主机 b 上层应用程序这里。

3. udp和tcp有什么区别?

  • tcp协议在传送数据段的时候要给段标号;udp协议不
  • tcp协议可靠;udp协议不可靠
  • tcp协议是面向连接;udp协议采用无连接
  • tcp协议负载较高,采用虚电路;udp采用无连接
  • tcp协议的发送方要确认接收方是否收到数据段(3次握手协议)
  • tcp协议采用窗口技术和流控制
特性 tcp udp
是否连接 面向连接 面向非连接
传输可靠性 可靠 不可靠
应用场合 传输大量数据 传输少量数据
速度

4. tcp传输的详细过程是怎样的?

进行三次握手,建立tcp连接。
  • 第一次握手:建立连接。客户端发送连接请求报文段,将syn位置为1,sequence number为x;然后,客户端进入syn_send状态,等待服务器的确认;
  • 第二次握手:服务器收到syn报文段。服务器收到客户端的syn报文段,需要对这个syn报文段进行确认,设置acknowledgment number为x+1(sequence number+1);同时,自己自己还要发送syn请求信息,将syn位置为1,sequence number为y;服务器端将上述所有信息放到一个报文段(即syn+ack报文段)中,一并发送给客户端,此时服务器进入syn_recv状态;
  • 第三次握手:客户端收到服务器的syn+ack报文段。然后将acknowledgment number设置为y+1,向服务器发送ack报文段,这个报文段发送完毕以后,客户端和服务器端都进入established状态,完成tcp三次握手。
    完成了三次握手,客户端和服务器端就可以开始传送数据。

ack:此标志表示应答域有效,就是说前面所说的tcp应答号将会包含在tcp数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。
tcp协议规定,只有ack=1时有效,也规定连接建立后所有发送的报文的ack必须为1。
syn(synchronization) : 在连接建立时用来同步序号。当syn=1而ack=0时,表明这是一个连接请求报文。对*同意建立连接,则应在响应报文中使syn=1和ack=1. 因此, syn置1就表示这是一个连接请求或连接接受报文。
fin (finis)即完,终结的意思, 用来释放一个连接。当 fin = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。

发送http请求,服务器处理请求,返回响应结果

tcp连接建立后,浏览器就可以利用http/https协议向服务器发送请求了。服务器接受到请求,就解析请求头,如果头部有缓存相关信息如if-none-match与if-modified-since,则验证缓存是否有效,若有效则返回状态码为304,若无效则重新返回资源,状态码为200.

关闭tcp连接
  • 第一次分手:主机1(可以使客户端,也可以是服务器端),设置sequence number和acknowledgment number,向主机2发送一个fin报文段;此时,主机1进入fin_wait_1状态;这表示主机1没有数据要发送给主机2了;
  • 第二次分手:主机2收到了主机1发送的fin报文段,向主机1回一个ack报文段,acknowledgment number为sequence number加1;主机1进入fin_wait_2状态;主机2告诉主机1,我“同意”你的关闭请求;
  • 第三次分手:主机2向主机1发送fin报文段,请求关闭连接,同时主机2进入last_ack状态;
  • 第四次分手:主机1收到主机2发送的fin报文段,向主机2发送ack报文段,然后主机1进入time_wait状态;主机2收到主机1的ack报文段以后,就关闭连接;此时,主机1等待2msl后依然没有收到回复,则证明server端已正常关闭,那好,主机1也可以关闭连接了。

5. 为什么很多站点第二次打开速度会很快?

主要原因是第一次加载页面过程中,缓存了一些耗时的数据。
那么,哪些数据会被缓存呢?

dns缓存

主要就是在浏览器本地把对应的 ip 和域名关联起来,这样在进行dns解析的时候就很快。

memorycache

是指存在内存中的缓存。从优先级上来说,它是浏览器最先尝试去命中的一种缓存。从效率上来说,它是响应速度最快的一种缓存。
内存缓存是快的,也是“短命”的。它和渲染进程“生死相依”,当进程结束后,也就是 tab 关闭以后,内存里的数据也将不复存在。

浏览器缓存

先看一张经典的流程图,结合理解
浏览器相关原理(面试题)详细总结一
浏览器缓存,也称http缓存,分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的情况下,才会走协商缓存。

强缓存

强缓存是利用 http 头中的 expirescache-control 两个字段来控制的。强缓存中,当请求再次发出时,浏览器会根据其中的 expires 和 cache-control 判断目标资源是否“命中”强缓存,若命中则直接从缓存中获取资源,不会再与服务端发生通信。

实现强缓存,过去我们一直用expires。当服务器返回响应时,在 response headers 中将过期时间写入 expires 字段。像这样

expires: wed, 12 sep 2019 06:12:18 gmt

可以看到,expires 是一个时间戳,接下来如果我们试图再次向服务器请求资源,浏览器就会先对比本地时间和 expires 的时间戳,如果本地时间小于 expires 设定的过期时间,那么就直接去缓存中取这个资源。

从这样的描述中大家也不难猜测,expires 是有问题的,它最大的问题在于对“本地时间”的依赖。如果服务端和客户端的时间设置可能不同,或者我直接手动去把客户端的时间改掉,那么 expires 将无法达到我们的预期。

考虑到 expires 的局限性,http1.1 新增了cache-control字段来完成 expires 的任务。expires 能做的事情,cache-control 都能做;expires 完成不了的事情,cache-control 也能做。因此,cache-control 可以视作是 expires 的完全替代方案。在当下的前端实践里,我们继续使用 expires 的唯一目的就是向下兼容。

cache-control: max-age=31536000

在 cache-control 中,我们通过max-age来控制资源的有效期。max-age 不是一个时间戳,而是一个时间长度。在本例中,max-age 是 31536000 秒,它意味着该资源在 31536000 秒以内都是有效的,完美地规避了时间戳带来的潜在问题。

cache-control 相对于 expires 更加准确,它的优先级也更高。当 cache-control 与 expires 同时出现时,我们以 cache-control 为准。

协商缓存

协商缓存依赖于服务端与浏览器之间的通信。协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。如果服务端提示缓存资源未改动(not modified),资源会被重定向到浏览器缓存,这种情况下网络请求对应的状态码是 304。

协商缓存的实现,从 last-modifiedetag,last-modified 是一个时间戳,如果我们启用了协商缓存,它会在首次请求时随着 response headers 返回:

last-modified: fri, 27 oct 2017 06:35:57 gmt

随后我们每次请求时,会带上一个叫 if-modified-since 的时间戳字段,它的值正是上一次 response 返回给它的 last-modified 值:

if-modified-since: fri, 27 oct 2017 06:35:57 gmt

服务器接收到这个时间戳后,会比对该时间戳和资源在服务器上的最后修改时间是否一致,从而判断资源是否发生了变化。如果发生了变化,就会返回一个完整的响应内容,并在 response headers 中添加新的 last-modified 值;否则,返回如上图的 304 响应,response headers 不会再添加 last-modified 字段。

使用 last-modified 存在一些弊端,这其中最常见的就是这样两个场景:

  • 我们编辑了文件,但文件的内容没有改变。服务端并不清楚我们是否真正改变了文件,它仍然通过最后编辑时间进行判断。因此这个资源在再次被请求时,会被当做新资源,进而引发一次完整的响应——不该重新请求的时候,也会重新请求。
  • 当我们修改文件的速度过快时(比如花了 100ms 完成了改动),由于 if-modified-since 只能检查到以秒为最小计量单位的时间差,所以它是感知不到这个改动的——该重新请求的时候,反而没有重新请求了。

这两个场景其实指向了同一个 bug——服务器并没有正确感知文件的变化。为了解决这样的问题,etag 作为 last-modified 的补充出现了。

etag 是由服务器为每个资源生成的唯一的标识字符串,这个标识字符串可以是基于文件内容编码的,只要文件内容不同,它们对应的 etag 就是不同的,反之亦然。因此 etag 能够精准地感知文件的变化。

etag 的生成过程需要服务器额外付出开销,会影响服务端的性能,这是它的弊端。因此启用 etag 需要我们审时度势。正如我们刚刚所提到的——etag 并不能替代 last-modified,它只能作为 last-modified 的补充和强化存在。

etag 在感知文件变化上比 last-modified 更加准确,优先级也更高。当 etag 和 last-modified 同时存在时,以 etag 为准。

service worker cache

service worker 是一种独立于主线程之外的 javascript 线程。它脱离于浏览器窗体,因此无法直接访问 dom。这样独立的个性使得 service worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 service worker 实现的离线缓存就称为 service worker cache。

service worker 的生命周期包括 install、active、working 三个阶段。一旦 service worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它。这是它可以用来实现离线存储的重要先决条件.

push cache

push cache 是指 http2 在 server push 阶段存在的缓存。这块的知识比较新,应用也还处于萌芽阶段,应用范围有限不代表不重要——http2 是趋势、是未来。在它还未被推而广之的此时此刻,我仍希望大家能对 push cache 的关键特性有所了解:

  • push cache 是缓存的最后一道防线。浏览器只有在 memory cache、http cache 和 service worker cache 均未命中的情况下才会去询问 push cache。
  • push cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。
  • 不同的页面只要共享了同一个 http2 连接,那么它们就可以共享同一个 push cache。

参考资料

  • 极客时间《浏览器工作原理与实践》
  • 掘金小册子《前端性能优化原理与实践》

最后