当前位置: 首页 > article >正文

linux协议栈网卡接收数据到tcp缓冲区

1. 核心数据结构与角色

数据结构所属层级作用
rx_ring网卡驱动层网卡的DMA环形缓冲区,存储原始数据包描述(物理地址由rx_buffer->dma指向)。
rx_buffer网卡驱动层描述DMA缓冲区的元数据(dma地址、page虚拟地址等)。
struct sockTCP/UDP层管理连接状态、数据队列(sk_receive_queuebacklogprequeue)。
struct socketBSD Socket层面向用户空间的抽象,关联struct sock,提供文件操作接口(如recv)。
sk_receive_queueTCP层存储已按序排列的数据包(sk_buff链表),供用户进程读取。
backlogTCP层临时缓存锁竞争时的数据包(当用户进程持有socket锁时)。
prequeueTCP层优化路径:用户进程正在读取时,数据直接暂存于此,避免锁竞争。

2. 数据流动的完整流程

阶段 1:网卡接收数据(硬中断)

  1. 网卡DMA写入内存

    • 网卡将数据包通过DMA写入驱动预分配的rx_ring缓冲区(物理地址由rx_buffer->dma指向)。

    • 数据位置:此时数据在内核的DMA映射区域rx_buffer->page对应的内存页)。

  2. 触发硬中断

    • 网卡触发硬中断(IRQ),中断处理函数(如igb_msix_ring)仅记录需处理的包数量,并触发软中断(NET_RX_SOFTIRQ)

    • 关键点:硬中断不处理数据,仅调度软中断。


阶段 2:软中断处理(协议栈入口)

  1. 软中断执行net_rx_action

    • 调用网卡驱动的poll函数(如igb_poll),从rx_ring中取出数据包。

    • 构建sk_buff

      • skb->data指向DMA缓冲区的数据(rx_buffer->page的虚拟地址)。

      • 填充协议头等元数据。

  2. 协议栈处理(IP/TCP层)

    • 数据包进入ip_rcv() → tcp_v4_rcv(),根据socket状态选择路径:

      • 路径A(无锁竞争):直接调用tcp_rcv_established()处理。

      • 路径B(用户进程持有锁):数据包暂存到backlog

      • 路径C(用户正在recv():数据包放入prequeue(性能优化)。


阶段 3:协议栈队列管理

(1)prequeue 路径(优化场景)
  • 条件:用户进程正在执行tcp_recvmsg()且未设置tcp_low_latency

  • 数据流

    复制

    软中断 → prequeue → tcp_recvmsg() → 用户空间
  • 优势:避免操作sk_receive_queue的锁竞争,数据直达用户进程。

(2)常规路径(sk_receive_queue
  • 条件:无锁竞争或prequeue不可用。

  • 数据流

    复制

    软中断 → tcp_data_queue() → sk_receive_queue → tcp_recvmsg() → 用户空间
  • 乱序处理:乱序数据暂存到out_of_order_queue,重组后移入sk_receive_queue

(3)backlog 路径(锁竞争场景)
  • 条件:用户进程持有socket锁(如正在send()recv())。

  • 数据流

    复制

    软中断 → backlog → 用户进程释放锁 → __release_sock() → sk_receive_queue → tcp_recvmsg()
  • 作用:防止数据包因锁竞争丢失。


阶段 4:用户进程读取数据

  1. tcp_recvmsg() 逻辑

    • sk_receive_queueprequeue中取出sk_buff

    • 调用skb_copy_datagram_iovec()将数据从内核(skb->data)拷贝到用户空间缓冲区。

  2. 内存拷贝次数

    • 最佳情况(零拷贝):用户空间直接映射DMA内存(如mmapsplice)。

    • 常规情况:1次拷贝(内核→用户空间)。


3. 关键交互关系图

plaintext

复制

网卡硬件
   │
   ↓ DMA
rx_ring (rx_buffer->dma) → 硬中断 → 软中断
   │                              │
   │                              ├─ 无锁竞争 → tcp_rcv_established() → sk_receive_queue → tcp_recvmsg()
   │                              │
   │                              ├─ 用户正在recv() → prequeue → tcp_recvmsg()
   │                              │
   │                              └─ 锁竞争 → backlog → 用户释放锁 → sk_receive_queue
   │
   └───────────────────────────────────────────────────────────┘

4. 性能优化要点

  1. 减少锁竞争

    • prequeue避免sk_receive_queue争用。

    • backlog防止数据丢失。

  2. 零拷贝技术

    • sendfile()/splice()直接传递DMA数据,避免内核-用户空间拷贝。

  3. 队列调优

    • 调整net.core.netdev_max_backlog(全局backlog长度)。

    • 启用tcp_low_latency=1(禁用prequeue,优先低延迟)。


5. 总结

  • rx_ringrx_buffer:网卡DMA的物理/虚拟内存管理。

  • 软中断:将DMA数据转为sk_buff,推动协议栈处理。

  • prequeue/backlog:解决用户进程与软中断的并发问题。

  • sk_receive_queue:最终存储有序数据,供用户进程读取。

  • struct socket:用户态接口,通过struct sock操作内核协议栈。

通过这种分层设计,Linux内核在保证可靠性的同时,实现了高性能的网络数据传输。


http://www.kler.cn/a/613704.html

相关文章:

  • 3.1go流程控制语句
  • 深度学习笔记19-YOLOv5-C3模块实现(Pytorch)
  • Python 爬虫:一键解锁 3GPP 标准协议下载难题
  • XCode16 在Other LInker Flags中,添加-ld64与不添加,有什么区别?
  • sql-labs靶场 less-1
  • hadoop客户端环境准备
  • Linux Shell 脚本使用YAD工具实现Shell图形化界面
  • SpringSecurity过滤器链:核心过滤器的执行顺序与职责
  • spring security设置多个数据源和登录验证码
  • 故障识别 | 基于改进螂优化算法(MSADBO)优化变分模态提取(VME)结合稀疏最大谐波噪声比解卷积(SMHD)进行故障诊断识别,matlab代码
  • MySQL Explain 分析 SQL 执行计划
  • 数据设计(范式、步骤)
  • 2025跳槽学习计划
  • 速卖通历史价格数据获取:API合规调用与爬虫方案风险对比
  • Maven版本统一管理
  • Cursor生成的UI太丑?如何减少UI拉扯?
  • WEB或移动端常用交互元素及组件 | Axure / 元件类型介绍(表单元件、菜单和表格 、流程元件、标记元件)
  • 一文详解k8s体系架构知识
  • 【Kafka】Kafka4.0在windows上启动
  • Android 蓝牙/Wi-Fi通信协议之:经典蓝牙(BT 2.1/3.0+)介绍