搜索
您的当前位置:首页正文

从输入 URL 到页面加载完成的过程中都发生了什么事情?

来源:二三娱乐

第一个问题:从输入 URL 到浏览器接收的过程中发生了什么事情?

从触屏到 CPU

首先是「输入 URL」,大部分人的第一反应会是键盘,不过为了与时俱进,这里将介绍触摸屏设备的交互。

当手指在这个传感器上触摸时,有些电子会传递到手上,从而导致该区域的电压变化,触摸屏控制器芯片根据这个变化就能计算出所触摸的位置,然后通过总线接口将信号传到 CPU 的引脚上。

左边是处理器,右边是触摸屏控制器,中间的 SDA 和 SCL 连线就是 I²C 总线接口。

CPU 内部的处理

移动设备中的 CPU 并不是一个单独的芯片,而是和 GPU 等芯片集成在一起,被称为 SoC(片上系统)。

前面提到了触屏和 CPU 的连接,这个连接和大部分计算机内部的连接一样,都是通过电气信号来进行通信的,也就是电压高低的变化,如下面的时序图:

除了计算,在 CPU 中还需要存储单元来加载和存储数据,这个存储单元一般通过触发器(Flip-flop)来实现,称为寄存器。

另外其实我也是刚开始学习 CPU 芯片的实现,所以就不在这误人子弟了,感兴趣的读者请阅读本节后面推荐的书籍。

从 CPU 到操作系统内核

前面说到触屏控制器将电气信号发送到 CPU 对应的引脚上,接着就会触发 CPU 的中断机制,以 Linux 为例,每个外部设备都有一标识符,称为中断请求(IRQ)号,可以通过/proc/interrupts文件来查看系统中所有设备的中断请求号,以下是 Nexus 7 (2013) 的部分结果:

shell@flo:/ $ cat /proc/interrupts

CPU0

17:          0      GIC  dg_timer

294:    1973609  msmgpio  elan-ktf3k

314:        679  msmgpio  KEY_POWER

因为 Nexus 7 使用了 ELAN 的触屏控制器,所以结果中的 elan-ktf3k 就是触屏的中断请求信息,其中 294 是中断号,1973609 是触发的次数(手指单击时会产生两次中断,但滑动时会产生上百次中断)。

130|shell@flo:/ $ getevent -lt /dev/input/event0

[  414624.658986] EV_ABS      ABS_MT_TRACKING_ID  0000835c

[  414624.659017] EV_ABS      ABS_MT_TOUCH_MAJOR  0000000b

[  414624.659047] EV_ABS      ABS_MT_PRESSURE      0000001d

[  414624.659047] EV_ABS      ABS_MT_POSITION_X    000003f0

[  414624.659078] EV_ABS      ABS_MT_POSITION_Y    00000588

[  414624.659078] EV_SYN      SYN_REPORT          00000000

[  414624.699239] EV_ABS      ABS_MT_TRACKING_ID  ffffffff

[  414624.699270] EV_SYN      SYN_REPORT          00000000

从操作系统 GUI 到浏览器

就这样,我们解答了第一个问题,不过由于时间有限,这里省略了很多细节,想进一步学习的读者推荐阅读以下书籍。

扩展学习

第二个问题:浏览器如何向网卡发送数据?

从浏览器到浏览器内核

在浏览器内核中会先查看缓存,然后设置 UA 等 HTTP 信息,接着调用不同平台下网络请求的方法。

需要注意浏览器和浏览器内核是不同的概念,浏览器指的是 Chrome、Firefox,而浏览器内核则是 Blink、Gecko,浏览器内核只负责渲染,GUI 及网络连接等跨平台工作则是浏览器实现的

HTTP 请求的发送

因为网络的底层实现是和内核相关的,所以这一部分需要针对不同平台进行处理,从应用层角度看主要做两件事情:通过 DNS 查询 IP、通过 Socket 发送数据,接下来就分别介绍这两方面的内容。

DNS 查询

;; global options: +cmd

;; Received 228 bytes from 8.8.8.8#53(8.8.8.8) in 220 ms

;; Received 503 bytes from 192.36.148.17#53(192.36.148.17) in 185 ms

;; Received 201 bytes from 192.48.79.30#53(192.48.79.30) in 1237 ms

;; Received 258 bytes from 61.135.165.235#53(61.135.165.235) in 2 ms

可能你在查询某些域名的时会发现和上面不一样,最底将看到有个奇怪的服务器抢先返回结果。。。

通过 Socket 发送数据

HTTP 常用的是 TCP 协议,由于 TCP 协议的具体细节到处都能看到,所以本文就不介绍了,这里谈一下 TCP 的 Head-of-line blocking 问题:假设客户端的发送了 3 个 TCP 片段(segments),编号分别是 1、2、3,如果编号为 1 的包传输时丢了,即便编号 2 和 3 已经到达也只能等待,因为 TCP 协议需要保证顺序,这个问题在 HTTP pipelining 下更严重,因为 HTTP pipelining 可以让多个 HTTP 请求通过一个 TCP 发送,比如发送两张图片,可能第二张图片的数据已经全收到了,但还得等第一张图片的数据传到。

另外,因为 HTTP 请求是纯文本格式的,所以在 TCP 的数据段中可以直接分析 HTTP 的文本,如果发现。。。

Socket 在内核中的实现

前面说到浏览器的跨平台库通过调用 Socket API 来发送数据,那么 Socket API 是如何实现的呢?

底层网络协议的具体例子

最底下是实际的二进制数据,中间是解析出来的各个字段值,可以看到其中最底部为 HTTP 协议(Hypertext Transfer Protocol),在 HTTP 之前有 54 字节(0x36),这就是底层网络协议所带来的开销,我们接下来对这些协议进行分析。

在 HTTP 之上是 TCP 协议(Transmission Control Protocol),它的具体内容如下图所示:

通过底部的二进制数据,可以看到 TCP 协议是加在 HTTP 文本前面的,它有 20 个字节,其中定义了本地端口(Source port)和目标端口(Destination port)、顺序序号(Sequence Number)、窗口长度等信息,以下是 TCP 协议各个部分数据的完整介绍:

0                  1                  2                  3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|          Source Port          |      Destination Port        |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|                        Sequence Number                        |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|                    Acknowledgment Number                      |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|  Data |          |U|A|E|R|S|F|                              |

| Offset| Reserved  |R|C|O|S|Y|I|            Window            |

|      |          |G|K|L|T|N|N|                              |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|          Checksum            |        Urgent Pointer        |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|                    Options                    |    Padding    |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|                            data                              |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

需要注意的是,在 TCP 协议中并没有 IP 地址信息,因为这是在上一层的 IP 协议中定义的,如下图所示:

IP 协议同样是在 TCP 前面的,它也有 20 字节,在这里指明了版本号(Version)为 4,源(Source) IP 为192.168.1.106,目标(Destination) IP 为119.75.217.56,因此 IP 协议最重要的作用就是确定 IP 地址。

因为 IP 协议中可以查看到目标 IP 地址,所以如果发现某些特定的 IP 地址,某些路由器就会。。。

但是,光靠 IP 地址是无法进行通信的,因为 IP 地址并不和某台设备绑定,比如你的笔记本的 IP 在家中是192.168.1.1,但到公司就变成172.22.22.22了,所以在底层通信时需要使用一个固定的地址,这就是 MAC(media access control) 地址,每个网卡出厂时的 MAC 地址都是固定且唯一的。

因此再往上就是 MAC 协议,它有 14 字节,如下所示:

最顶上的 Frame 是代表 Wireshark 的抓包序号,并不是网络协议

就这样,我们解答了第二个问题,不过其实这里面还有很多很多细节没介绍,建议大家通过下面的书籍进一步学习。

扩展学习

第三个问题:数据如何从本机网卡发送到服务器?

从内核到网络适配器(Network Interface Card)

连接 Wi-Fi 路由

Wi-Fi 网卡需要通过 Wi-Fi 路由来与外部通信,原理是基于无线电,通过电流变化来产生无线电,这个过程也叫「调制」,而反过来无线电可以引起电磁场变化,从而产生电流变化,利用这个原理就能将无线电中的信息解读出来就叫「解调」,其中单位时间内变化的次数就称为频率,目前在 Wi-Fi 中所采用的频率分为 2.4 GHz 和 5 GHz 两种。

而同样基于无线电原理的 2G/3G/LTE 也会遇到类似的问题,但它并没有采用 Wi-Fi 那样的独占方案,而是通过频分(FDMA)、时分(TDMA)和码分(CDMA)来进行复用,具体细节这里就不展开了。

运营商网络内的路由

主干网间的传输

既然是基于光来传输数据,数据传输速度也就取决于光的速度,在真空中的光速接近于 30 万千米/秒,由于光纤包层(cladding)中的折射率(refractive index)为 1.52,所以实际光速是 20 万千米/秒左右,从首都机场飞往广州白云机场的距离是 1967 千米,按照这个距离来算需要花费 10 毫秒才能抵达。这意味着如果你在北京,服务器在广州,等你发出数据到服务器返回数据至少得等 20 毫秒,实际情况预计是 2- 3 倍,因为这其中还有各个节点路由处理的耗时,比如我测试了一个广州的 IP 发现平均延迟为 60 毫秒。

这个延迟是现有科技无法解决的(除非找到超过光速的方法),只能通过 CDN 来让传输距离变短,或尽量减少串行的来回请求(比如 TCP 建立连接所需的 3 次握手)。

IDC 内网

这里的带宽成本很高,是按照峰值来结算的,以每月每 Gbps(注意这里指的是 bit,而不是 Byte)为单位,北京这边价格在十万人民币以上,一般网站使用 1G 到 10G 不等。

接下来光纤中的数据将进入集群(Cluster)交换机,然后再转发到机架(Rack)顶部的交换机,最后通过这个交换机的端口将数据发往机架中的服务器,可以参考下图(来自 Open Compute):

上图左边是正面,右边是侧面,可以看到顶部为交换机所留的位置。

需要注意的是,一般网络书中提到的交换机都只具备二层(MAC 协议)的功能,但在 IDC 中的交换器基本上都具备三层(IP 协议)的功能,所以不需要有专门的路由了。

服务器 CPU

扩展学习

第四个问题:服务器接收到数据后会进行哪些处理?

为了避免重复,这里将不再介绍操作系统,而是直接进入后端服务进程,由于这方面有太多技术选型,所以我只挑几个常见的公共部分来介绍。

负载均衡

请求在进入到真正的应用服务器前,可能还会先经过负责负载均衡的机器,它的作用是将请求合理地分配到多个服务器上,同时具备具备防攻击等功能。

LVS

LVS 的作用是从对外看来只有一个 IP,而实际上这个 IP 后面对应是多台机器,因此也被成为 Virtual IP。

前面提到的 NAT 也是一种 LVS 中的工作模式,除此之外还有 DR 和 TUNNEL,具体细节这里就不展开了,它们的缺点是无法跨网段,所以百度自己开发了 BVS 系统。

反向代理

反向代理是工作在 HTTP 上的,具体实现可以基于 HAProxy 或 Nginx,因为反向代理能理解 HTTP 协议,所以能做非常多的事情,比如:

进行很多统一处理,比如防攻击策略、防抓取、SSL、gzip、自动性能优化等

应用层的分流策略都能在这里做,比如对 /xx 路径的请求分到 a 服务器,对 /yy 路径的请求分到 b 服务器,或者按照 cookie 进行小流量测试等

缓存,并在后端服务挂掉的时候显示友好的 404 页面

监控后端服务是否异常

⋯⋯

Nginx 的代码写得非常优秀,从中能学到很多,对高性能服务端开发感兴趣的读者一定要看看。

Web Server 中的处理

请求经过前面的负载均衡后,将进入到对应服务器上的 Web Server,比如 Apache、Tomcat、Node.JS 等。

以 Apache 为例,在接收到请求后会交给一个独立的进程来处理,我们可以通过编写 Apache 扩展来处理,但这样开发起来太麻烦了,所以一般会调用 PHP 等脚本语言来进行处理,比如在 CGI 下就是将 HTTP 中的参数放到环境变量中,然后启动 PHP 进程来执行,或者使用 FastCGI 来预先启动进程。

(等后续有空再单独介绍 Node.JS 中的处理)

进入后端语言

Web 框架(Framework)

如果你的 PHP 只是用来做简单的个人主页「Personal Home Page」,倒没必要使用 Web 框架,但如果随着代码的增加会变得越来越难以管理,所以一般网站都会会基于某个 Web 框架来开发,因此在后端语言执行时首先进入 Web 框架的代码,然后由框架再去调用应用的实现代码。

读取数据

这部分不展开了,从简单的读写文件到数据中间层,这里面可选的方案实在太多。

扩展学习

第五个问题:服务器返回数据后浏览器如何处理?

从 01 到字符

HTTP 请求返回的 HTML 传递到浏览器后,如果有 gzip 会先解压,然后接下来最重要的问题是要知道它的编码是什么,比如同样一个「中」字,在 UTF-8 编码下它的内容其实是「11100100 10111000 10101101」也就是「E4 B8 AD」,而在 GBK 下则是「11010110 11010000」,也就是「D6 D0」,如何才能知道文件的编码?可以有很多判断方法:

用户设置,在浏览器中可以指定页面编码

HTTP 协议中

中的 charset 属性值

对于 JS 和 CSS

对于 iframe

如果在这些地方都没指明,浏览器就很难处理,在它看来就是一堆「0」和「1」,比如「中文」,它在 UTF-8 下有 6 个字节,如果按照 GBK 可以当成「涓枃」这 3 个汉字来解释,浏览器怎么知道到底是「中文」还是「涓枃」呢?

这样后续对文本的操作就是基于「字符」(Character)的了,一个汉字就是一个字符,不用再关心它究竟是 2 个字节还是 3 个字节。

外链资源的加载

(待补充,这里有调度策略)

JavaScript 的执行

从字符到图片

二维渲染中最复杂的要数文字显示了,虽然想想似乎很简单,不就是将某个文字对应的字形(glyph)找出来么?在中文和英文中这样做是没问题的,因为一个字符就对应一个字形(glyph),在字体文件中找到字形,然后画上去就可以了,但在阿拉伯语中是不行的,因为它有有连体形式。

(以后续再单独介绍,这里非常复杂)

跨平台 2D 绘制库

(以后再单独介绍,Skia 内部实现调用层级太多,直接讲代码可能不适合初学者)

GPU 合成

(以后续再单独介绍,虽然简单来说就是靠贴图,但还得介绍 OpenGL 以及 GPU 芯片,内容太长)

扩展学习

这节内容是我最熟悉,结果反而因为这样才想花更多时间写好,所以等到以后再发出来好了,大家先可以先看看以下几个站点:

第六个问题:浏览器如何将页面展现出来?

前面提到浏览器已经将页面渲染成一张图片了,接下来的问题就是如何将这张图片展示在屏幕上。

Framebuffer

以 Linux 为例,在应用中控制屏幕最直接的方法是将图像的 bitmap 写入/dev/fb0文件中,这个文件实际上一个内存区域的映射,这段内存区域称为 Framebuffer。

需要注意的是在硬件加速下,如 OpenGL 是不经过 Framebuffer 的。

从内存到 LCD

LCD 显示

最后简单介绍一下 LCD 的显示原理。

首先,要想让人眼能看见,就必须有光线进入,要么通过反射、要么有光源,比如 Kindle 所使用的 E-ink 屏幕本身是不发光的,所以必须在有光线的地方才能阅读,它的优点是省电,但限制太大,所以几乎所有 LCD 都会自带光源。

在上图中可以看到 6 盏 LED,这就是整个屏幕的光源,这些光源将通过反射的反射输出到屏幕中。

有了光源还得有色彩,在 LED 中通常做法是使用彩色滤光片(Color filter)来将 LED 光源转成不同颜色。

从上图可以看到每 3 种颜色的滤光片都全亮的时候就是白色,都灭就是黑色,如果你仔细看还能看到有些点并不是完全黑,这是字体上的反锯齿效果。

通过这 3 种颜色亮度的不同组合就能产生出各种色彩,如果每个颜色点能产生 256 种亮度,就能生成 256 * 256 * 256 = 16777216 种色彩。

如何控制这些颜色点的亮度?这就要靠液晶体了,液晶体的特性是当有电流通过时会发生旋转,从而将部分光线挡住,所以只要通过电压控制液晶体的转动就能控制这个颜色点的亮度,目前手机屏幕中通常使用 TFT 控制器来对其进行控制,在 TFT 中最著名的要数 IPS 面板。

当光线进入眼睛后,接下来就是生物学的领域了,所以我们到此结束。

扩展学习

本文所忽略的内容

为了编写方便,前面的介绍中将很多底层细节实现忽略了,比如:

内存相关

栈,函数调用,已经有很多优秀的文章或书籍介绍了

内存映射,动态库加载等

队列几乎无处不在,但这些细节和原理没太大关系

各种缓存

CPU 的缓存、操作系统的缓存、HTTP 缓存、后端缓存等等

各种监控

很多日志会保存下来以便后续分析

FAQ

从微博反馈来看,有些问题被经常问到,我就在这里统一回答吧,如果有其它问题请在评论中问。

Q:学那么多有什么用?根本用不着

A:计算机是人类最强大的工具,你不想了解它是如何运作的么?

Q:什么都了解一点,还不如精通一项吧?

A:非常认同,初期肯定需要先在某个领域精通,然后再去了解周边领域的知识,这样还能让你对之前那个领域有更深刻的理解。

Q:晒出来培养一堆面霸跟自己过不去?

A:本文其实写得很浅,每个部分都能再深入展开。

Q:这题要把人累死啊,说几天都说不完的

A:哈哈哈,大神你暴露了,题目只是手段,目的是将你这样的大牛挖掘出来。

大家的讨论

非常感谢各位大牛的参与讨论,这里搜集了其中的一些回答。

@WOODHEAD笨笨:请求被送往本地路由,接入商路由,旁路分析是否违法地址,连接被中断,浏览器无辜得显示网页不存在。严重的有人来查水表

caoz: 这不是我的面试题么! 还有一道题,用户反应我们网站卡,请问都有哪些可能性,以及排方法。

@唐福林:与时俱进,现在应该问从打开app到刷新出内容,整个过程中都发生了什么,如果感觉慢,怎么定位问题,怎么解决

@寒冬winter: 回复@Ivony:这题胜在区分度高,知识点覆盖均匀,再不懂的人,也能答出几句,而高手可以根据自己擅长的领域自由发挥,从URL规范、HTTP协议、DNS、CDN、到浏览器流式解析、CSS规则构建、layout、paint、onload/domready、JS执行、JS API绑定⋯⋯

@莴怖熵崴箔:这种就是流氓问题,我还想问从你按了键盘到屏幕上出现字符,中间都发生了什么事,提示一下:设想你是一个电子。哦,不对,电子又是什么

@ils传言:不提电厂发电机转了几圈的也干掉!//@Philonis高:不提交换机和路由器工作原理的全干掉!//@南非蜘蛛:从7层协议的角度说会比较全面。这种问题只有全栈工程师才能回答。

@耸肩的阿特拉斯阁下:DNS解析URL出IP/Port,浏览器连接并向此地址发出GET请求,web服务端(nginx、apache)接收到请求后,通过CGI等接口协议调用动态语言(php等),动态语言再连接数据库查询相应数据并处理,然后反馈给浏览器,浏览器解析反馈页面,通过html、javascript、css处理后呈现到屏幕⋯⋯每个细节的话估计要800页的书

@一棹凌烟:这种面试题在系统领域的招聘里其实简单好使。还有一个类似的:从在键盘上敲下一个字符键开始,到在虚拟机里的terminal里显示出来,中间的过程是什么?

@ICT_朱亚东:记得6年前上胡伟武的芯片设计课,老胡第一节课就说,上完这门课,我希望你们能搞清楚,我翻了一页PPT,计算机内部都做了那些流水操作,当然啦,我是一点都不记得了。

@julyclyde:我们运维一般问一个TCP segment in a IP packet in an ethernet frame经过一个路由器之后发生什么变化

@西西福厮:从浏览器说起,操作系统相应键盘中断,事件队列处理,到互联网路由,到服务器网卡中断,到最后输出缓冲。。。细说能说两小时。

@Xscape:从键盘中断说起?回车前的预解析都很靠后了..//@纯白色燃烧: 从键盘到弹簧入万有引力而后直达量子力学。

@Bosn:然后从硬件再到电子⋯⋯量子…薛定谔之猫…平行宇宙⋯⋯乃至万能的哲学!!

@imPony:可深入到PN结中的电子流动层面

@yuange1975:我算对整个过程比较清楚,包含服务器的处理,web服务器和浏览器的处理以及安全问题,估计少有对两者的安全都研究过的。但面试时要清晰的比较完整的把大块流程列出来说明白,也有难度。估计也很难有机会时间去整理文章了。

@ShopEx王磊:我也问这个问题题好多年, 或者变通一下:从输入URL到展现, 都涉及到哪些缓存环节, 缓存的更新机制是怎样的

@一棹凌烟:这种面试题在系统领域的招聘里其实简单好使。还有一个类似的:从在键盘上敲下一个字符键开始,到在虚拟机里的terminal里显示出来,中间的过程是什么?

@智慧笨蛋: 确实可以维度不同的说,主要还是看颗粒度,光网络这段从wifi 解密,到NAT,到局间交换,ip包在以太网包映射等等就可以写一本书了

最后

细心的读者应该会发现本文有隐藏内容,请找。。。

Top