当前位置: 首页 > 科技观察

你好奇Linux是如何发送和接收网络数据包的吗?

时间:2023-03-19 13:08:11 科技观察

前言这一次,我要重点说一个问题。Linux系统如何收发网络包?文本网络模型为了使多个设备能够通过网络相互通信,解决网络互连中各种设备的兼容性问题,国际标准化组织制定了开放系统互连通信参考模型(openSystemInterconnectionReferenceModel),即OSI网络模型,主要有7层,即应用层、表示层、会话层、传输层、网络层、数据链路层和物理层。每一层负责不同的功能,具体如下:应用层,负责为应用提供统一的接口;表示层,负责将数据转换成与另一个系统兼容的格式;会话层,负责建立、管理和终止表示层实体之间的通信会话;传输层,负责端到端的数据传输;网络层,负责数据路由、转发、分片;数据链路层,负责数据帧封装、错误检测、MAC寻址;物理层负责在物理网络中传输数据帧;由于OSI模型过于复杂,仅提出了概念层次,并没有给出具体的实施方案。其实我们比较普遍也比较实用的是四层模型,也就是TCP/IP网络模型。Linux系统就是按照这种网络模型来实现网络协议栈的。TCP/IP网络模型有4层,分别是应用层、传输层、网络层和网络接口层。各层的功能如下:应用层负责为用户提供一套应用程序,如HTTP、DNS、FTP等;传输层,负责端到端的通信,如TCP、UDP等;网络层,负责网络数据包的封装、分片、路由、转发,如IP、ICMP等;网络接口层,负责网络数据包在物理网络中的传输,如网络数据包的封框、MAC寻址、错误检测、网络帧通过网卡的传输等;TCP/IP网络模型比OSI网络模型更简单、更容易记住。它们之间的关系如下图所示:但是,我们常说的七层和四层负载均衡是用OSI网络模型来描述的。七层对应应用层,四层对应传输层。Linux网络协议栈我们可以把自己的身体比作应用层的数据,底层的衣服比作传输层的TCP头,外套比作网络层的IP头,帽子和鞋子比作网络中的框架接口层头和帧尾。冬天的时候,我们在家要出门游玩,自然要先穿上打底衣,再穿上保暖外套,最后戴上帽子和鞋子再出门。当数据包发送出去时,应用层数据会根据网络协议栈逐层封装处理。从下图可以看出,应用层数据在每一层的封装格式。其中:传输层在应用数据的前面加上一个TCP头;网络层在TCP数据包的前面加上IP头;网络接口层在IP数据包的前后添加帧头和帧尾;每一层都有自己的协议头,增加了自然网络包的大小,但是物理链路不能传输任何大小的数据包,所以在以太网中,最大传输单元(MTU)规定为1500字节,即,指定了单个传输的最大IP数据包大小。当网络包超过MTU的大小时,会在网络层进行分片,保证分片后的IP包不会超过MTU的大小。MTU越小,需要的子包越多,网络吞吐量越差,反之,如果MTU越大,需要的子包越小,网络吞吐量就越好。了解了TCP/IP网络模型和网络数据包的封装原理之后,你一定猜到了Linux网络协议栈的模样。它实际上类似于TCP/IP的四层结构:从上图Stack中的网络协议可以看出:应用程序需要通过系统调用与Socket层交换数据;Socket层下面是传输层、网络层和网络接口层;底层是网卡驱动和硬件网卡设备;Linux中接收网络包的过程网卡是计算机中的一块硬件,负责接收和发送网络包。当网卡收到一个网络包时,会通过DMA技术将网络包放入RingBuffer中。这是内核内存中NIC驱动程序中的环形缓冲区。操作系统收到网络包后,应该如何告诉操作系统网络包已经到达?最简单的方法是触发中断,即每当网卡收到网络包时,触发中断告诉操作系统。但是,有一个问题。在高性能网络场景下,网络包的数量会非常多,会触发很多中断。要知道,当CPU接收到中断时,它会停止正在做的事情并进行处理。这些网络包处理完之后,又会回去继续其他事情,所以频繁触发中断会导致CPU继续处理中断,其他任务可能无法继续,从而影响系统的整体效率。因此,为了解决频繁中断带来的性能开销,Linux内核在2.6版本引入了NAPI机制。它是一种混合的“中断和轮询”方法来接收网络数据包。它的核心理念是不间断地阅读。数据,而是先使用中断唤醒服务程序进行数据接收,然后使用poll方法轮询数据。例如,当一个网络数据包到达时,网卡发起一个硬件中断,然后执行网卡的硬件中断处理函数。中断处理函数处理完后,需要“暂时屏蔽中断”,然后唤醒“软中断”轮询处理数据,直到没有新的为止。只有在数据中断时才恢复中断,这样可以一次处理多个网络数据包,从而减少网卡中断带来的性能开销。软中断是如何处理网络数据包的?它会将RingBuffer中的数据复制到内核structsk_buffbuffer中,这样就可以将其作为网络包交给网络协议栈进行逐层处理。首先,它会先进入网络接口层。在这一层,它将检查消息的合法性。如果不合法,它将被丢弃。如果合法,它会找出网络数据包的上层协议类型,如IPv4或IPv6,然后去除帧头和帧尾,然后交给网络层。在网络层,取出IP包,判断网络包的下一步,比如是交给上层处理还是转发。当确认网络包要发往本机时,会从IP头中检查上层的协议类型是TCP还是UDP,然后去掉IP头,再交给本机传输层。传输层取出TCP头或UDP头,根据四元组“源IP、源端口、目的IP、目的端口”为标识,找出对应的Socket,复制数据到接收缓冲区插座。最后应用层程序调用Socket接口从内核的Socket接收缓冲区中读取新传入的数据给应用层。至此,一个网络包的接收过程结束。从下图左侧也可以看到接收网络包的过程。右边部分正好相反,是发送网络包的过程。Linux中发送网络包的过程是上图的一半,发送网络包的过程正好与接收过程相反。首先,应用程序会调用Socket接口发送数据包。由于这是一个系统调用,在内核态会从用户态落到Socket层,Socket层会将应用层数据复制到Socket发送缓冲区。接下来,网络协议栈从Socket发送缓冲区中取出数据包,按照TCP/IP协议栈从上到下逐层处理。如果使用TCP传输协议发送数据,会在传输层加上TCP头,然后交给网络层。网络层会在数据包中添加一个IP包,然后通过查询路由表,根据MTUShard大小确定下一跳的IP。分片后的网络数据包会被送到网络接口层,通过ARP协议获取下一跳的MAC地址,然后添加帧头和帧尾,放入发包队列。这些准备好后,会触发一个软中断,告诉网卡驱动有新的网络包要发送。最后,驱动程序通过DMA从发包队列中读取网络数据包,放入硬件网卡的队列中。然后物理网卡将其发送出去。综上所述,计算机通常与网卡、交换机、路由器等网络设备相连。由于网络设备的异构性,国际标准化组织定义了一个七层的OSI网络模型,但是这个模型比较复杂,在实际应用中并没有使用,而是采用了更加简化的TCP/IP模型,Linux网络协议栈就是按照这个模型实现的。TCP/IP模型主要分为四层:应用层、传输层、网络层和网络接口层。每一层负责不同的职责,这也是Linux网络协议栈的主要组成部分。当应用程序通过Socket接口发送数据包时,数据包会被网络协议栈从上到下逐层处理,然后发送到网卡队列,然后网卡将网络包发送出去.当接收到一个网络数据包时,也必须在网络协议栈中从底部到顶部逐层处理,然后再发送给应用程序。