丢包通常源于内核协议栈或驱动异常(如校验失败、缓冲区溢出、软中断延迟),可用perf定位函数级问题;先用tcpdump确认丢包位置,再通过perf trace和record分析ICMP生命周期及高开销路径,结合/proc/net/snmp指标验证根因。

这种情况往往不是网络层丢包,而是内核协议栈或驱动在处理特定数据包时出错(比如校验失败、缓冲区溢出、软中断不及时),perf 可以帮你定位到具体函数甚至指令级的异常行为。
确认丢包发生在本机协议栈
先排除物理链路和中间设备干扰:用 tcpdump -i eth0 'icmp and icmp[icmptype] == icmp-echo' 抓包,对比发送端发出的 echo request 和本机收到的是否一致。如果发了 100 个但 tcpdump 只捕获到 92 个,说明丢包发生在网卡驱动或更前(如硬件过滤);如果 tcpdump 捕获齐全但应用层没收到 reply,则问题在 IP 层或 ICMP 处理路径(如 netfilter、路由查找、socket 接收队列溢出)。
用 perf trace 监控 ICMP 相关系统调用和内核事件
运行以下命令持续观察 ICMP 包的生命周期:
perf trace -e 'syscalls:sys_enter_sendto,syscalls:sys_exit_sendto,syscalls:sys_enter_recvfrom,syscalls:sys_exit_recvfrom,napi:napi_poll,net:netif_receive_skb,net:net_dev_queue,icmp:icmp_rcv' -a sleep 30
重点关注:
• icmp_rcv 是否被频繁触发但无对应 sendto 返回(说明进来了但没回包)
• napi_poll 耗时是否突增或调用次数骤减(软中断处理瓶颈)
• netif_receive_skb 有输入但 icmp_rcv 缺失(可能被 iptables DROP 或校验失败静默丢弃)
用 perf record 捕获高开销函数(重点看软中断和内存路径)
在复现丢包期间运行:
perf record -e 'irq:softirq_entry,irq:softirq_exit,kmem:kmalloc,kmem:kfree,skb:consume_skb,icmp:icmp_rcv' -g --call-graph dwarf -a sleep 60
然后分析:
perf report -g --no-children | grep -A5 -B5 "icmp_rcv\|napi\|softirq"
常见线索:
• icmp_rcv 下游调用 ip_local_deliver_finish → __inet_lookup 耗时长 → 可能 conntrack 表爆炸或哈希冲突
• consume_skb 出现在 napi_poll 中但频率远低于 netif_receive_skb → SKB 未被及时消费,接收队列堆积
• kmalloc 失败或慢速路径频繁触发 → 内存碎片或 slab 不足(尤其 skbuff_head_cache)
结合 /proc/net/snmp 和 /proc/net/netstat 验证假设
丢包前后执行:
netstat -s -p icmp | grep -E "(In|Out)Cmp" && cat /proc/net/snmp | grep -A1 "Icmp\|Ip"
关键指标:
• IcmpInErrors 上升 → 校验失败、长度错误等硬性丢包
• IcmpInDestUnreachs 异常增多 → 路由或本地端口不可达(可能因 socket 队列满)
• UdpInCsumErrors 或 TcpInCsumErrors 同步上升 → 网卡 offload 设置错误(如 ethtool -K eth0 rx off tx off 关闭校验卸载后对比)








