Linux入站UDP包偶发延迟问题该如何定位分析?
问题背景
故障现象
有时tcpdump捕获到UDP包的接收会被延迟到下一个UDP包入站时才触发,但network tap设备显示线缆上的报文传输无延迟。
运行场景
部署在Linux用户空间的Profinet协议栈通过raw sockets建立了循环连接,每4ms收发一次Profinet报文。另有一个线程通过UDP socket每约30ms接收一次UDP报文并立即回复,当前系统CPU负载约10%。偶发出现接收的UDP报文卡在网络驱动的情况,直到2秒后下一个UDP包入站时,之前滞留的UDP包和新到的包才会同时被接收,全程无丢包。
测试方案
- 执行命令
tcpdump -i eth0 --time-stamp-precision=nano --time-stamp-type=adapter_unsynced -w /tmp/tcpdump.pcap将UDP流量记录到RAM磁盘的文件中。 - 同时使用network tap设备镜像记录线缆上的流量。
网络拓扑
搭载Linux系统、带eth0接口的嵌入式设备 <---> tap设备 <---> PLC
tcpdump运行在嵌入式设备上,tap设备监听线缆传输内容,Profinet连接实际运行在PLC和嵌入式设备之间,另有一台PC连接tap设备记录其监听到的流量。
问题解答
1. 延迟定位方法及关联系统机制
已知触发该现象的系统效应
该问题是Linux网络栈的NAPI(New API)中断合并+网卡接收批处理的典型表现,嵌入式场景下尤为常见:
- 网卡默认开启中断合并配置时,收到报文后不会立刻触发硬中断通知内核,会等待更多报文凑批处理,或达到超时阈值才上报。如果后续长时间没有新报文触发中断,已收到的报文就会一直积压在网卡队列。
- 高频raw socket Profinet流量会抢占网卡中断的调度优先级,UDP报文的软中断处理被延后,直到下一个UDP报文到达触发新的中断,队列中积压的所有报文才会被一次性上送协议栈。
- 若UDP socket设置了
SO_RCVLOWAT参数,接收缓冲区数据量未达到阈值时不会唤醒用户态进程,也会表现为报文延迟到下一批报文到达时才被读取。
定位步骤
- 检查网卡配置:执行
ethtool -k eth0查看gro、lro等接收卸载开关状态,执行ethtool -c eth0查看rx-usecs、rx-frames等中断合并参数,确认是否设置了大于0的延迟阈值。 - 内核路径trace:使用
ftrace跟踪netif_receive_skb、udp_rcv、sock_def_readable等函数的调用时间点,判断报文积压发生在网卡硬件队列、内核协议栈处理阶段,还是用户态进程唤醒阶段。 - 检查socket状态:执行
ss -aum查看对应UDP端口的rcvlowat配置,以及接收队列的实时积压情况。 - 对比验证:临时关闭中断合并和接收卸载,执行
ethtool -C eth0 rx-usecs 0 rx-frames 1、ethtool -K eth0 gro off lro off,测试问题是否复现。
2. tcpdump时间戳的层级与生成阶段
你执行的命令指定了--time-stamp-type=adapter_unsynced,该模式下时间戳由网卡硬件在报文物理层接收完成时生成,对应OSI模型的物理层,时间戳精度取决于网卡内部时钟,和系统时钟无关。
如果未指定时间戳类型,tcpdump默认使用内核生成的时间戳,在网卡硬中断处理完成后、内核将skb送入网络协议栈的__netif_receive_skb_core阶段生成,对应OSI模型的数据链路层,此时报文尚未进入三层处理流程。
注意:tcpdump的时间戳和用户态进程是否读取报文无关,只要报文被网卡/内核捕获就会打时间戳。你场景中tcpdump记录的时间戳存在延迟,说明报文确实在网卡硬件队列或中断调度阶段被积压,并非用户态读取慢导致。
内容的提问来源于stack exchange,提问作者falkb

