一、网络协议栈

用户态:应用程序

内核态:

系统调用

socket(定位到tcp)

tcp、udp

ip

mac
网卡驱动程序

硬件:网卡

二、数据包的形态

应用程序:data

tcp:segment

ip:packet

mac:frame

协议栈:sk_buffer(tcp、ip、mac数据包都是该数据结构)

三、发送数据

1.TCP

send/write:data

内核将data分段成多个sk_buffer (根据MSS:即MCU-相关头)

sk_buffer存到发送缓存区

克隆一份sk_buffer (注意:不是拷贝!!!)

加TCP头(sk_buffer)

加IP头(sk_buffer)

加MAC(帧头)头、帧尾(sk_buffer)

触发软中断:网卡驱动程序把sk_buffer写到DMA内存区域(ring_buffer)

触发网卡发送数据,如果发送成功,ringbuffer里面的数据和sk_buffer都清除

如果收到ack,删除发送缓存区的sk_buffer。

2. UDP

sendto

相比于TCP:

  • 没有分段
  • 没有发送缓存区
  • 没有克隆
  • 可能有分片(数据大于MSS时,IP会分片)
  • 没有ack,如果发送成功直接就把sk_buffer删除了

四、接收数据

1. TCP

recv/read

网卡收到数据,DMA写ringbuffer

硬件中断,CPU屏蔽硬件中断,触发软中断

(类比硬件中断是敲门,屏蔽硬件中断意味着把门打开,让来的人都进来,不必再敲门了。等我把门关了,才能继续触发硬件中断)

取出ringbuffer,即数据帧(sk_buffer)

....

存到接收缓存区(回发ack)

2. UDP

recvfrom

相比于TCP

  • 可能IP会重组(如果发送时有IP分片的话)
  • 不会回发ack

五、冷知识

1. TCP分段原因

TCP有分段的原因(MSS):方便重传特定包,避免IP分片和重组

UDP没有分段和发送缓存区是因为它没有重传机制!!!

注意:TCP包仍可能被IP层分片的情况(当帧大于MTU就会发生):

  • 路径 MTU 发现(PMTU)失效
  • TCP 或 IP 首部添加额外选项
  • 故意发送超过 MSS 的段(罕见)


不过不影响重传机制,发送缓存区的sk_buffer没收到对应序号的ack还是会触发重传。
只是IP分片可能会让重传的概率变大了而已(IP重组时如果有分片缺失,会把剩余的分片都扔了,这样重传的TCP包就多了)

2. 克隆、拷贝、浅拷贝

维度克隆(Clone)拷贝(深拷贝,Deep Copy)
资源共享共享核心数据,复制元信息(对象的控制结构)不共享任何资源,全量复制
独立性共享数据的修改可能相互影响(有限制)完全独立,修改互不影响
开销低(无需拷贝大量数据)高(需拷贝全部数据)
核心目的高效创建共享数据的轻量副本创建完全独立的副本

拷贝的一种特殊情况:
“浅拷贝”(仅复制对象的表层结构,如指针,不复制指针指向的底层数据),这时候浅拷贝与克隆的行为类似(共享底层数据)。但两者的核心区别在于:

  • 克隆通常会通过引用计数、写时复制等机制主动管理共享资源的生命周期,避免悬垂指针或重复释放;
  • 浅拷贝往往缺乏这种管理,可能导致内存安全问题(如原对象释放后,副本的指针变成悬垂指针)。

克隆的例子:

  • Linux 内核中 sk_buff 的克隆(skb_clone):新的 skb 复制原 skb 的协议头指针、长度等元信息,但共享同一个数据缓冲区(payload),通过引用计数(users)管理数据的生命周期。
  • 虚拟机克隆:克隆的虚拟机可能共享原虚拟机的基础镜像文件(只读),仅记录自身的修改(如 COW 技术,写时复制),而非复制整个磁盘数据。

更多推荐