TCP/UDP 数据路径:从用户态到对端应用的拷贝与流转

张开发
2026/4/10 9:19:34 15 分钟阅读

分享文章

TCP/UDP 数据路径:从用户态到对端应用的拷贝与流转
TCP/UDP 数据路径从用户态到对端应用的拷贝与流转本文从Linux 典型协议栈视角说明应用send/recv与TCP/UDP如何协同内核经IP、邻居解析、网络设备与网卡 DMA到达链路并在对端自底向上交付到应用归纳CPU 拷贝与 DMA的分工以及TCP 接收端不read时的流控与零窗口行为。函数名为示意具体内核版本与路径可能略有差异以当前内核源码为准。目录TCP/IP 分层与 Linux 协议栈大致对应以太网上的封装顺序示意端到端总览发送端用户态 → 网卡发送路径栈Mermaid链路与对端接收接收路径栈Mermaidepoll 就绪到 recv谁唤醒、谁拷贝拷贝次数小结TCP 与 UDP 差异简表常见工程优化应用不读取时 TCP 接收缓冲区会怎样零窗口与背压示意ss 中 Recv-Q / Send-Q 的含义延伸阅读免责声明TCP/IP 分层与 Linux 协议栈大致对应TCP/IP 概念层Linux 中常见对应概括应用层用户进程、socketAPI传输层tcp_*、udp_*、inet_*网络层ip_*、ipv6_*、路由、分片链路层邻居ARP/NDP、netdevice、qdisc、驱动物理层网卡、PHY、介质本文重点在socket → 驱动 → DMA与对端逆路径。以太网上的封装顺序示意典型IPv4 TCP在以太网链路上的帧结构仅示意字段顺序长度因选项与 MTU 而异┌──────────────┬─────────┬─────────┬──────────────────────┐ │ 以太网头 │ IPv4 头 │ TCP 头 │ 载荷应用数据 │ │ (含 MAC) │ │ │ │ └──────────────┴─────────┴─────────┴──────────────────────┘UDP则将中间TCP 头换为UDP 头更短、无连接状态。端到端总览本端应用 send/sendto/write → 系统调用进入内核 → socket 层校验、构造 msghdr → 传输层TCPtcp_sendmsg/ UDPudp_sendmsg → IP 层封装、路由、按需分片 → 邻居子系统ARP/NDP 解析下一跳 L2 地址 → netdevice / qdisc入队 → 驱动 ndo_start_xmitDMA 到网卡 → 物理链路 → 对端网卡 DMA 入内核 → 软中断/NAPI → 协议栈上行 → 对端 TCP/UDP → socket → recv/read 拷回用户缓冲区接收端网络发送端CPU 拷贝至少 1 次DMADMA→内核CPU 拷贝至少 1 次用户态缓冲区内核 socket / sk_buff网卡 DMA链路/路由网卡 DMA内核接收缓冲用户态缓冲区发送端用户态 → 网卡1. 系统调用入口示例send(sockfd, buf, len, 0)/sendto(...)。CPU陷落到内核态进入socket子系统如sock_sendmsg→ 按协议族进入inet_sendmsg等。2. Socket 层概括校验文件描述符与 socket 状态。按类型分派SOCK_STREAM → TCP、SOCK_DGRAM → UDP。组装struct msghdr将数据交给传输层。3. 传输层TCP 与 UDPTCPtcp_sendmsg等路径发送队列 / 发送缓冲用户数据通常先拷贝进内核sk_buff链表发送队列由 TCP 分段、拥塞控制、重传、按序等逻辑驱动实际发包。语义并非每次send都立即对应网线上的一个报文可能合并、延后发送。拷贝用户缓冲区 → 内核 sk_buff至少一次CPU 拷贝特殊零拷贝路径除外。UDPudp_sendmsg等路径语义面向报文无 TCP 式的字节流保证各sendto往往对应在 MTU 允许下独立数据报的封装与发送路径无连接状态机与重传。内核仍会构造sk_buff并排队到出口路径「无缓冲」一般指无 TCP 那样复杂的流式可靠发送状态机并非绝对零队列。4. IP 层封装IPv4/IPv6首部查路由表选定出接口。DF MTU可能触发分片IPv4或由 PMTUD/路径行为导致发送策略变化IPv6 通常避免中段分片。5. 邻居子系统与设备层ARP / NDP解析下一跳MAC。dev_queue_xmit进入队列规则qdisc与驱动发送路径。6. 网卡与 DMA驱动ndo_start_xmit将待发帧交给网卡现代网卡常用DMA从内核内存拉取描述符指向的数据。「网卡侧零拷贝」指在到达 DMA 前数据已在内核可达的物理/连续内存中由网卡直接读而不是再起一道 CPU 把整帧逐字节抄到网卡片上小缓冲区实现细节依硬件。发送路径栈Mermaid用户态 send/writesocket 层TCP / UDPIP 层邻居 ARP/NDPnetdevice / qdisc驱动 ndo_start_xmit网卡 DMA链路与对端接收链路比特经交换机/路由器逐跳转发。对端网卡DMA写入预分配的环形缓冲等内核内存区。硬中断 → 软中断 / NAPI收包线程化处理进入__netif_receive_skb一类路径。以太网解复用 → IP校验、去分片、本机投递。TCPtcp_v4_rcv/ UDPudp_rcvTCP查找 socket、校验、重组、ACK、填入接收缓冲区应用read/recv再从内核缓冲拷入用户缓冲。UDP按五元组等匹配 socket校验和进入接收队列应用recvfrom再拷贝到用户态。Socket 层sock_recvmsg完成「内核缓冲 → 用户缓冲」的至少一次 CPU 拷贝同样存在高级零拷贝例外。接收路径栈Mermaid网卡 DMA 写入 ring软中断 / NAPI链路层解复用IP 层TCP / UDPsocket 接收队列用户态 recv/readepoll 就绪到 recv谁唤醒、谁拷贝epoll只负责多路复用告诉你「哪些 fd 上发生了你关心的事件」真正把数据从内核 socket 接收缓冲拷到用户态的仍是read/recv/recvmsg。典型顺序如下。进程在epoll_wait上阻塞或超时返回。对端数据经DMA → NAPI → TCP/UDP → socket 接收队列入队后内核将该 socket 标记为可读并把对应epitem链入epoll 的就绪链表或按边沿/水平触发策略通知。epoll_wait返回events[i].events含EPOLLIN等。应用调用recv走sock_recvmsg等路径完成内核接收缓冲 → 用户缓冲区的 CPU 拷贝与是否使用 epoll 无关。收包路径 NAPI→TCP内核 socket / TCPepoll 实例用户态应用收包路径 NAPI→TCP内核 socket / TCPepoll 实例用户态应用epoll_ctl(ADD, sockfd, EPOLLIN)epoll_wait() 阻塞数据入接收队列 sk_rcvbuf标记 fd 可读就绪链表/通知返回events 含 EPOLLINrecv/read内核缓冲 → 用户缓冲CPU 拷贝边沿触发ET提示epoll_wait返回后应循环读到EAGAIN否则内核可能认为「已通知过」而不再重复报告直到新数据到达。拷贝次数小结阶段典型情况用户 → 内核发送至少1 次 CPU 拷贝常规send内核协议栈内部尽量复用 sk_buff、减少多余拷贝具体依路径内核内存 → 网卡多为DMA对 CPU 而言非「再拷一整帧」意义下的拷贝网卡 → 对端内核DMA入主机内存内核 → 用户接收至少1 次 CPU 拷贝常规recvTCP 与 UDP 差异简表维度TCPUDP可靠 / 顺序是在连接语义下否流控与拥塞控制有无由应用自行处理发送侧语义字节流可合并分段、重传报文尽力交付常规路径下用户↔内核拷贝与 UDP同属经典两跳模型发用户→内核收内核→用户同左常见工程优化技术作用sendfile等减少「文件页缓存 → socket」之间的用户态参与与拷贝次数大页、mmap降低 TLB 压力或改变内存映射方式需结合场景评估多队列网卡、RSS并行化收包与中断负载GRO / LRO 等收包侧合并/卸载减轻 CPU调试时需知可能改变抓包形态XDP、eBPF、DPDK更早/旁路处理报文降低内核协议栈开销复杂度高SO_ZEROCOPY等视内核与协议在限定条件下减少拷贝需查当前内核与网卡支持应用不读取时 TCP 接收缓冲区会怎样当对端应用长期不read/recv内核接收缓冲堆积到达的段被确认并放入socket 接收队列用户态不取走则占用越来越大。接收窗口缩小对端在ACK中通告的TCP Window随剩余缓冲减小发送端据此减速或停发。零窗口Zero Window缓冲满时通告rwnd0发送端应停发数据零窗口探测除外形成背压。发送端表现同步阻塞send可能阻塞非阻塞返回EAGAIN/EWOULDBLOCK。若发送缓冲也满则同样阻塞或失败。极端与边界rmem_max等达到上限后行为依赖内核策略丢弃、压力等对端可能重传。进程退出则内核关闭 socket可能对端收到RST。Keepalive主要检测死连接一般不替代「读走数据」。运维辨别ss -ntp/ss -nup观察Recv-Q、Send-QRecv-Q 大常表示应用读得慢或内核排队多。零窗口与背压示意发送端应用TCP 对端栈内核接收缓冲接收端应用发送端应用TCP 对端栈内核接收缓冲接收端应用直至接收端 read 腾出空间ACK 携带更大窗口长期不 read缓冲渐满ACK 中 rwnd 缩小至 0send 阻塞或 EAGAINss 中 Recv-Q / Send-Q 的含义列TCPLISTENTCP已建立UDPRecv-Q已完成三次握手、等待accept的连接队列长度受backlog等影响内核已收到、应用尚未 read的字节数排队待取接收队列中报文或字节统计实现细节见内核版本Send-Q通常意义较小或依实现已交给内核发送路径、尚未完全确认离开或应用侧仍占用的待发数据量与窗口、状态相关发送侧排队情况解读时建议结合ss -nti输出中的cwnd、rwnd等扩展信息若支持与tcpdump对照。延伸阅读同仓库内可与TCPIP协议栈详解、TCP拥塞控制算法详解对照阅读与ss相关的笔记见Linux ss 命令详解与 Netlink 原理。免责声明不同 OSBSD、Windows与内核版本在函数名、零拷贝能力、sysctl 默认值上均有差异生产排障请结合ss、抓包、tcpdump与官方文档。主题Linux 网络栈、TCP、UDP、DMA、流控、零窗口。

更多文章