计算机网络(4)

计算机网络(4)

基础篇

Linux系统收发网络

  1. 网络模型
    国际标准化组织规定了开放式系统通信参考模型(Open System Interconnection Reference Model),该模型主要有七层:应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层。
    由于OSI模型太复杂,提出的只是概念理论上的分层,没有提供具体的实现方案。
    实际应用的是四层模型,即 TCP/IP 网络模型,Linux 系统正是按照这套网络模型来实现网络协议栈的。

  2. Linux网络协议栈
    应用程序通过系统调用来跟Socket层进行数据交互
    Socket层的下面就是传输层、网络层和网络接口层
    最下面的一层则是网卡驱动程序和硬件网卡设备

Linux网络协议栈

  1. Linux接受网络包流程
    网卡专门负责接受和发送网络包,当网卡接收到一个网络包后,通过DMA技术,将网络包写入指定内存地址(Ring Buffer),这是一个环形缓冲区,接着就会告诉操作系统这个网络包已经到达。

怎么告诉操作系统网络包到达了呢?
最简单的一种方式就是触发中断,也就是每当网卡收到一个网络包,就触发一个中断告诉操作系统。但是这样的话,在高性能网络场景下,网络包的数量会非常多,那么就会触发非常多的中断,而cpu收到这些中断就会停下手里的事情,去处理这些网络包,处理完毕后,才会回去继续其他事情,那么频繁地触发中断,会导致cpu一直没完没了的处理中断,影响系统的整体效率。
解决方案:引入NAPI机制,混合中断和轮询的方式来接受网络包,它的核心就是不采用中断的方式读取数据,而是采用中断唤醒数据接受的服务程序,然后通过poll方法来轮询数据。
因此,当有网络包达到时,通过DMA技术,将网络包写入到指定的内存,接着网卡向CPU发起硬件中断,cpu收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。
硬件中断处理函数会做如下的事情:

  • 暂时屏蔽中断,表示已经知道内存中有数据了,告诉网卡下次再收到数据包直接写入内存,不要再通知cpu,避免cpu不停的被中断,提高效率。
  • 接着,发起软中断,然后回复刚才屏蔽的中断。
    软中断由软中断处理函数处理,硬件中断处理函数做的工作很少,主要耗时的工作都交给软中断处理函数了。

软中断的处理:
内核中的 ksoftirqd 线程专门负责软中断的处理,当 ksoftirqd 内核线程收到软中断后,就会轮询处理数据:从 Ring Buffer 中获取一个数据帧,用 sk_buff 表示,从而可以作为一个网络包交给网络协议栈进行逐层处理。

网络协议栈

网络包的收发

注意:到了网络层,取出 IP 包后,需要判断网络包下一步的走向,比如是交给上层处理还是转发出去,确认这个网络包发送给本机后,就会从IP头里查看上一层协议类型是TCP还是UDP,接着去掉IP头,然后交给传输层。

  1. Linux 发送网路包的流程
    如上图右半部分,首先应用程序调用 Socket 发送数据包的接口(系统调用,进入内核态中的Socket层),内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入发送缓冲区。
    接下来,网络协议栈从Socket发送缓冲区中取出sk_buff,并按照TCP/IP协议栈从上到下逐层处理。

注意:

  • 用TCP传输协议发送数据,会先拷贝一个新的 sk_buff 副本。因为 sk_buff 最后到达网卡完成发送时,sk_buff 会被释放掉。但是TCP协议是支持丢失重传的,在收到对方的ACK之前,这个sk_buff 不能删除。所以实际上传递出去的是 sk_buff 的一个拷贝,等到 ACK 再真正删除。

  • sk_buff 可以表示各个层的数据包:在应用层叫data,在TCP层称为segment,IP层叫packet,数据链路层称为 frame。也就是说全部数据包只用一个结构体来描述,是为了上下层之间传递数据时避免多次拷贝,降低CPU效率。

  • 怎么实现全部数据包只用一个结构体?通过指针,例如:

    • 收到报文时,从网卡驱动开始,通过协议栈层层往上传送数据包,通过增加 skb -> data 的值,来逐步剥离协议首部。
    • 发送报文时,创建 sk_buff 结构体,数据缓存区的头部预留足够的空间,用来填充各层首部,在经过各下层协议时,通过减少 skb -> data 的值来增加协议首部。

数据包的表示

  • 当数据发送完成以后,工作还没有结束,因为内存还没有清理。当发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 和 RingBuffer 内存。最后,当这个TCP报文的ACK应答时,传输层会释放原始的sk_buff。

发送网络数据的时候,涉及几次内存拷贝操作
第一次,调用发送数据的系统调用时,内核会申请一个内核态的sk_buff内存,将用户待发送的数据拷贝到sk_buff内存,并将其加入到发送缓冲区。
第二次,使用TCP传输协议的情况下,从传输层进入网络层的时候,每一个sk_buff都会复制一份副本,实际发送的是副本,原始的sk_buff会保留到收到这个数据包的ACK,为了实现TCP可靠传输
第三次,当IP层发现sk_buff大于MTU时才会进行。会再申请额外的sk_buff,并将原来的sk_buff拷贝为多个小的sk_buff。

posted @ 2025-07-11 23:44  waterme  阅读(13)  评论(0)    收藏  举报