1. 基本概念
网卡:网卡是计算机里的一个硬件,专门负责接收和发送网络包,当网卡接收到一个网络包后,会通过 DMA 技术,将网络包写入到指定的内存地址,接着就会告诉操作系统这个网络包已经到达。
OSI 网络模型:该模型主要有 7 层,每一层负责的职能都不同,如下
应用层,负责给应用程序提供统一的接口;
表示层,负责把数据转换成兼容另一个系统能识别的格式;
会话层,负责建立、管理和终止表示层实体之间的通信会话;
传输层,负责端到端的数据传输;
网络层,负责数据的路由、转发、分片;
数据链路层,负责数据的封帧和差错检测,以及 MAC 寻址;
物理层,负责在物理网络中传输数据帧;
TCP/IP 网络模型:共有 4 层,每一层负责的职能如下
应用层,负责向用户提供一组应用程序,比如 HTTP、DNS、FTP 等;
传输层,负责端到端的通信,比如 TCP、UDP 等;
网络层,负责网络包的封装、分片、路由、转发,比如 IP、ICMP 等;
网络接口层,负责网络包在物理网络中的传输,比如网络包的封帧、 MAC 寻址、差错检测,以及通过网卡传输网络帧等;
Linux中使用的主要还是TCP/IP网络模型
2. 收发包过程
- 当网络包到达网卡时,网卡会通过DMA方式将其放入环形缓冲队列,即RingBuffer,它是网卡在启动时分配和初始化的环形缓冲队列。如果RingBuffer已满,新的数据包将被丢弃。
- 当DMA操作完成时,网卡会向CPU发起一个硬中断,表明有网络包到达。CPU根据中断表,调用注册的中断处理函数。网卡硬中断响应程序会先暂时屏蔽中断,避免 CPU 被不停中断。然后为网络包创建内核数据结构sk_buffer,并将网络包数据拷贝到sk_buffer中。接着发起软中断请求,通知内核有新的网络包到达,并恢复之前屏蔽的中断
- 内核中的ksoftirqd (每个CPU会绑定一个ksoftirqd内核线程专门用来处理软中断响应)线程专门负责软中断的处理,当 ksoftirqd 发现有软中断请求到来,将调用网卡驱动注册的函数(一般是poll函数),poll函数将sk_buffer中的网络数据包送到网络协议栈中。
- 当数据包到达网络协议栈时,首先会经过网络接口层的处理,该层会检查报文的合法性,如果不合法则丢弃,合法则会确定该网络包的上层协议类型(如IPv4或IPv6),然后去掉帧头和帧尾,最后交给网络层处理。
- 数据包到达网络层时,内核会取出数据包的IP头,判断该数据包下一跳的走向,如果数据包是发送给本机的,则取出传输层的协议类型(TCP或者UDP),并去掉数据包的IP头,将数据包交给上图中的传输层处理。
- 在传输层中,TCP协议对应内核协议栈中注册的tcp_rcv函数,UDP协议对应内核协议栈中注册的udp_rcv函数。以TCP协议为例,数据包到达传输层时,会由内核协议栈中的tcp_rcv函数处理,在tcp_rcv函数中去掉TCP头,并根据四元组(源IP,源端口,目的IP,目的端口)查找对应的Socket,将网络数据包中的传输数据拷贝到Socket中的接收缓冲区中。
- 最后,由用户态的应用层程序通过系统调用read读取Socket接收缓冲区中的数据,如果接收缓冲区中没有数据,那么应用程序就会在系统调用上阻塞,直到Socket接收缓冲区有数据,将内核的 Socket 接收缓冲区的数据「拷贝」到应用层的缓冲区,然后唤醒用户进程,应用程序读取数据。
- 发包过程则相当于收包过程反向进行,其中,如果使用的是 TCP协议发送数据,那么内核会先拷贝一个新的 sk_buff 副本进行传输,这是因为 TCP 协议是支持重传的,在收到对方的 ACK报文之前,这个 sk_buff 需要保留以保证后续可以重新发送。所以每次发送的时候,实际上传输的是 sk_buff 的一个拷贝,等收到 ACK 再真正删除。
3. 性能开销
收包开销:
- 应用程序通过系统调用read读取网络数据时,需要从用户态转为内核态
- 数据接收完成后,系统调用返回时从内核态转为用户态。
- 网络数据从内核空间拷贝到用户空间。
- 内核线程ksoftirqd响应软中断。
- 网卡数据到达后,CPU响应硬中断。
- DMA拷贝网络数据包到内存。
发包开销:
- 和接收数据一样,应用程序在调用系统调用send的时候会从用户态转为内核态
- 发送完数据后,系统调用返回时从内核态转为用户态。
- 网卡发送完数据,CPU响应硬中断。
- 在内核协议栈的传输层中,TCP协议对应的发送函数将用户要发送的数据拷贝到sk_buffer中。
- 从传输层到网络层的时候,拷贝sk_buffer副本。
- 在网络层,如果要发送的数据大于MTU,则会进行分片操作。