专栏名称: 高效运维
高效运维公众号由萧田国及朋友们维护,经常发布各种广为传播的优秀原创技术文章,关注运维转型,陪伴您的运维职业生涯,一起愉快滴发展。
目录
相关文章推荐
InfoQ架构头条  ·  Cloudflare在11月发生重大故障,导 ... ·  5 天前  
51好读  ›  专栏  ›  高效运维

90%的人都以为自己知道:浏览器中输入一个URL会发生神马?真相Wanna Cry

高效运维  · 公众号  · 运维  · 2017-05-16 07:11

正文

文末有小活动

本文授权转自公众号:西加加语言(xjj267)

作者:吳YH堅


前言

前几天看到一个题目,问在浏览器中输入一个URL会发生神马,这好像是网上比较流行的面试题,而且也被回答烂了,仔细想下来,要是自己遇到这个题目能不能答上来,后来一想,这个题目要完全答出来可以把大学开始的几乎所有知识都用上。

呵呵,为了模拟面试,所以都凭印象说的,没有查网络资料,所以估计很多地方都不对,欢迎来拍。

后续可以总结一个非常非常详细的版本,个人感觉真的可以把大学开始的所有的东西都用上,包括连数电模电(全部流程都跑不掉),信息论(gzip 压缩),密码学(HTTPS),通讯原理(网络基带传输)这种都可以,本文还是以软件处理过程为主。

1、硬件层---键盘到CPU

输入网址嘛,那就是键盘输入了,键盘一般用 usb 或者 PS/2 口连接电脑,现在见过 PS/2 口的人不多了吧,我们就看看 usb 吧,usb 分为主从模式,一般的键盘本身的 usb 是一个从设备,相应的还有u盘啊,鼠标啊都是从设备,对应的主机端的 USBcontroller 是一个主设备,这个主设备挂在CPU上,在X86结构的机器上是挂在主板的南桥(好像是的,反正是低速的那个)芯片上,如果是 ARM 这类嵌入式设备的话,那么就是挂在APB总线上,对这个我熟悉一些,就按 ARM 这种 SoC 来说吧,当是在手机上输入URL吧,呵呵。

键盘输入字符以后,通过 USB 协议(USB协议简单的来说就是一个串行协议,靠一个时钟电信号和一个数据电信号传输数据)将数据传输到 APB 总线上的 USB 控制器上。

然后这个控制器开始抢总线,总线的 APB 桥接收到信号以后也开始通过 APB 总线上的时钟和数据线发送一个中断信号到 APB 桥中,APB 桥同时连接在 AHP 高速总线上,接收完数据以后,它开始抢高速总线,高速总线上有个 Arbiter (总线仲裁器)。

因为高速总线上接的都是内存啊这种家伙,你一个小小的 USB 不见得抢得过他们,等仲裁器给你时间以后,APB 桥开始把中断信号发给 CPU。

CPU接收到这个中断以后,要是没有其他更高级的中断需要处理的话就会调用内核中的中断处理函数开始处理中断,中断处理函数又分为底半部和顶半部。

首先会是底半部处理,把中断放到处理队列中就返回了,然后顶半部从队列根据注册的的中断号拿到自己对应的中断号进行处理,这时候中断处理函数就算收到这个信号了,相当于硬件的电信号已经传递到软件层了,中断处理函数会调用 USB 的驱动程序去USB控制器上读取这个输入的字符,这个字符还是通过两条总线(APB和AHB)以独占的方式传递到软件层上,其实呢,是软件控制这个信号写到了内存的某个空间上。

整个流程是这样的:

发出中断 :键盘--->usb控制器 ---> APB桥 ---> CPU硬件中断 ---> 中断处理底部 ---> 中断处理函数顶部 ---> 驱动程序 ---> 读取数据 ---> CPU寻址 ---> 写入内存

2、内核层到应用层

假设我们输入的 url 以C语言的 scanf 函数为例,scanf 函数会一直在内核态阻塞着,直到中断处理函数接收到了所有的字符并且接受到了一个回车符,这时 scanf 语句会通过操作系统的 read 调用把缓冲区的所有字符从内核态拷贝到用户态,这时这些个字符就从电信号传递到了应用程序(浏览器)的软件层了。

整个流程是

scanf ---> 系统调用read ---> 内核态阻塞等待 ---> 读取拷贝数据 ---> 用户态接收数据

因为软件层也不是一个实体层次,其实所有的操作最后都变成了硬件的电信号,所有没办法把流程写得很明白,大概就是这么个意思吧。

3、网络请求

应用程序(浏览器)接收到这个 URL 以后,就开始走网络层来获取数据了,这里我们就不深入到底层的电信号了,就按网络这一套来说吧。

4、DNS解析

首先就要解析域名了,浏览器会先看一下本地的 host 文件,看有没有 url 域名对应的ip地址,如果没有的话,会通过 DNS 协议发送数据包给 DNS 服务器询问域名的ip地址,DNS 服务自己没有对应的ip的话会继续往上寻找,然后把ip地址发回给浏览器。

DNS 协议是 UDP 的,但好像有特殊情况是 TCP 连接,好像是各个 DNS 服务器之间传输用的 TCP 吧,不管了,反正通过 UDP 这个传输层协议之上的 DNS 应用层协议把url对应的ip地址得到了。

5、传输数据准备

拿到 ip 地址以后,假设我们输入的网址是 https://www.baidu.com/,百度的服务器在北京,而我们在上海,浏览器就通过 connect 开始连接这个北京的 ip 地址了,连接跑到 tcp/ip 协议栈以后,协议栈一看这个 IP 和本地 IP 根本就不在一个段上,那 ARP 也不发了,直接把数据包转发到网关上,也就是家里的路由器上了;

路由器拿到地址,查一下本地的路由表,呀,我这小路由表上没这个网段的啊,那我不管了,给我的上级路由吧(也许是小区的大路由器了),小区路由一看,我这也没有啊,这样一级一级传递到了上海的城际路由上,然后到了北京的城际路由,这么一层一层下来终于传到了百度的服务器上。

在这里看上去简单,但是路由的算法是很复杂的,这一块我也不太懂没弄过,但是 Dijkstra 选路算法还是明白的,别问我为什么记得这个单词,大学计算机网络课的课程作业啊,就是实现这个 Dijkstra 啊,通过路由算法,终于把第一个数据包发送给百度的服务器了。

6、建立连接

这个数据包是神马呢,就是我们熟悉的 TCP 三次握手的第一握了,SYN=1 的那个数据包,关于三次握手就不多说了,大家都知道,关键是我不记得细节了,反正就是大家都懂的,握手完了连接就建立完了。为什么建立连接需要三次握手,关闭连接需要四次握手,呵呵,写不动了。

同时,在百度服务器这边,有个 web 服务器的应用程序(比如 Nginx),一直在监听着80端口,收到这个数据包以后也同时和客户端建立起了连接了。

7、传输数据

连接建立好以后,是 TCP 连接建立了,这时通过更上层的 HTTP 协议开始传输数据,HTTP 协议本身很复杂,这里我们简单的来说,就是浏览器发送了一个 GET / HTTP/1.1 字符串给了服务器,服务器接受到这个字符串以后,通过协议栈传递给了上层的 Nginx 应用程序。

应用程序一看这个地址是/,然后对照一下自己的配置文件 nginx.conf,有个配置告诉他碰到这种请求,直接把本地的 index.html 吐出去,于是 Nginx 读取 index.html 并把数据通过这条 TCP 连接回复给了客户端。

由于 http 协议是无状态的,所以本次传输完数据以后,这个链接就关闭了。

8、完成显示

好了,后面的就是接受到数据的显示了,关于 webki t内核的细节就真不清楚了,就是解 DOM 树,执行JS代码,重新请求 CSS 和图片,然后把页面画出来吧。

哦了,题目做完了,其实还有很多细节没有说的,比如网络寻址的时候的 ARP 协议啊,路由器寻路的其他很多算法啊,Nginx 处理请求的时候的几个阶段解读啊,是否需要反向代理到后端的服务器上啊,最后显示的时候还需要操作显卡进行显存的操作,如果页面中有复杂的图形效果还需要 GPU 参与运算之类的,要写就写不完了。

扫码二维码,关注“西加加语言”

【小活动】


你们面试时遇到过哪些奇怪的、奇葩的难题?

欢迎留言,看看谁的最奇葩,用面试题互相伤害吧~

近期好文

《77%的Linux运维都不懂的内核问题》

《10年反黑风云:Linux下的安全攻防实录》

《DevOps创始导师首次访华内容全曝光,传播最正统的理念和方法》

《腾讯QQ日请求12亿的运营平台到底有多diao(三声)?》

《一个关于Paxos算法的故事》

《8种常被忽视的SQL错误用法》

《腾讯1300场NBA直播背后的技术力量》

《5分钟让你了解 ZooKeeper 的原理》

点击“阅读原文”,享受 GOPS·北京站 特价优惠