计算机网络的底层到底是怎么实现的?
从主机的视角看来,计算机网络一共有两个计算单元,一个是主机的CPU,另外一个就是网卡的硬件芯片,这两个计算单元是相互独立运行的。其中主机的CPU负责处理OSI参考模型的三到七层,网卡的硬件芯片负责处理OSI参考模型的一到二层。
当你使用Socket编程时,你所编写的代码主要集中在五到七层,剩下的三到四层已经被Socket封装起来了,就是你知道Socket函数的语法,但是不知道Socket函数内部的实现细节。但是无论是你编写的代码,还是Socket代码,只要所产生的Packet没有进入网卡,那么就一直由主机的CPU负责计算处理。一旦产生的Packet进入网卡,那么剩下的计算全部由网卡的硬件芯片负责计算与处理。
主机在将Packet递交给网卡硬件芯片时,会将目的MAC、以及packet是什么协议类型都会一一告知。这样网卡的硬件芯片就可以独立自主完成剩余的计算。完成Packet的封装以及CRC计算,最后将Packet完成Encode、Modulation,并最终发送到线路上。
信号的接收方将信号Decode,Demodulation,并校验CRC。如果没有问题,将接收到的Packet的二层信息剥离,将完整的Packet放入缓冲区。
如果事情就这么结束,那么接收方的CPU永远都拿不到数据! 为了让接收方主机拿到数据,需要网卡硬件芯片给主机CPU一个硬件中断信号。
由于来自网卡硬件芯片中断优先级比较高,可以中断CPU正在处理的任务,让CPU执行与中断向量对应的中断处理程序,并将硬件网卡缓冲区的Packet复制到主机CPU控制的内存,最终进入TCP/IP控制的全局缓冲区。
接下来Packet就会在TCP/IP内部流转,TCP/IP根据packet是TCP/UDP/ICMP来决定Packet的走向。如果是那你编写的Socket对应的进程的packet, TCP/IP很容易发现这一点,因为根据目的端口号,很容易就找到你的进程号。当你的进程尝试读取数据时,你要读取的数据已经静静地呆在Socket对应的缓冲区里了。
一旦读取完毕,缓冲区立马释放,留给后续的packet使用。
如果接收方是一台路由器,Packet在TCP/IP内部流转时,发现Packet并不是自己的。于是查询路由表完成后续转发。
但是这样会出现一个大问题。每次硬件网卡接收到一个Packet都会触发一次硬件中断。而这种高优先级的硬件中断会强行挂起主机CPU正在处理的任务。造成CPU利用率超高,路由器一秒钟处理几十万、几百万个Packet,要硬件中断同样的次数,想想都很可怕。
为了避免每次接收Packet,都触发一次硬件中断,需要修改这种中断逻辑。CPU提前将转发表下发到硬件芯片,那么收到Packet之后硬件芯片直接查转发表就完成了转发,压根不需要中断路由器的CPU。
当然如果目的IP= 路由器接口,那么还是需要中断路由器的CPU,因为不中断不行啊!毕竟流量是自己的,需要TCP/IP完成流转,并最终到达目的进程。