深夜,监控告警再次响起,屏幕上刺眼的丢包率曲线让人心头一紧。你熟练地登录服务器,cat /proc/net/dev,目光瞬间锁定在网卡统计信息里那个不断跳动的“dropped”计数上。这个数字像一枚无声的信号弹,它告诉你问题确实存在,但源头,可能远比你想象的要复杂。
“丢包”并非简单的网络断开
很多人第一眼看到“dropped”,脑海里浮现的就是数据包在网络上“消失”的画面。这种理解对了一半,但内核语境下的“dropped”远比这精细。它特指数据包已经成功抵达网络接口,并被网卡驱动接收,但在后续的协议栈处理流程中,因为某种资源限制或策略决策而被内核主动丢弃。这与你用ping测试时因为路由不通而产生的“100% packet loss”有本质区别——后者是包根本到不了你的主机。
核心瓶颈:积压队列的溢出
最常见的“dropped”元凶,是接收队列(rx ring buffer)或协议栈积压队列(backlog)的溢出。想象一下,网卡就像一条高速流水线的入口,它把收到的包裹(数据帧)暂时放在门口的小货架(接收环形缓冲区)上。内核的中断服务例程(ISR)或NAPI(New API)机制则负责把这些包裹搬进仓库(内核内存)。
问题来了:如果包裹来的速度太快,超过了搬运工的处理能力,或者仓库门口的临时堆放区(积压队列)满了,新到的包裹就无处可放。内核没时间也没空间处理它,只能无奈地标记为“dropped”,然后把它扔进垃圾箱。这种丢弃发生在数据链路层或网络层的入口,用ethtool -S eth0 | grep rx_dropped或ip -s link show看到的正是这个阶段的统计。
另一种“主动”丢弃:策略与过滤
除了被动的资源耗尽,内核也会基于规则主动说“不”。这通常发生在网络层(IP层)或传输层(TCP/UDP层)。例如:
- 防火墙(Netfilter/iptables, nftables):配置了DROP规则的数据包,会被计入相应的计数器。
- 反向路径过滤(Reverse Path Filtering, rp_filter):为了防止IP欺骗,内核会检查数据包的源地址是否可以从接收网卡路由出去。如果检查失败,默认会丢弃包,并可能在内核日志(
dmesg)中留下“martian source”的痕迹。 - TCP层校验和错误或无效状态:如果一个TCP包校验和不对,或者到达了一个已经关闭的连接,内核会直接丢弃。这部分计数可以在
/proc/net/snmp文件的“Tcp”行中的“InErrs”字段找到关联。
如何定位“dropped”的真凶?
看到dropped计数增长,别慌,按图索骥。首先,用ip -s link show dev eth0确认是接收(RX)还是发送(TX)方向的丢弃,以及具体的数量级。接着,分两步走:
第一步,排查资源瓶颈。检查接收队列大小:ethtool -g eth0。如果“Current hardware settings”的值很小(比如默认的256),而流量又很大,尝试在业务低峰期适度调大:ethtool -G eth0 rx 4096。同时,观察系统CPU使用率,特别是软中断(top查看%si)是否过高,这可能表明内核处理网络中断的“搬运工”忙不过来了。
第二步,审查过滤规则。快速查看防火墙规则:iptables -L -n -v 或 nft list ruleset,关注DROP和REJECT规则的计数器。检查rp_filter设置:sysctl -a | grep \.rp_filter,在需要多网卡或复杂路由的环境下,可能需要将其调整为宽松模式(值为2)。
曾经有一个案例,dropped计数在每秒几百个的水平徘徊,调整队列和中断亲和性都收效甚微。最后在dmesg -T里发现了蛛丝马迹:“kernel: nf_conntrack: table full, dropping packet”。原来是连接跟踪表满了,防火墙的state模块无法为新连接建立跟踪条目。通过sysctl -w net.netfilter.nf_conntrack_max=524288增大表项上限,问题迎刃而解。
数字背后的系统哲学
dropped计数的存在,本身就体现了Linux内核的一种设计哲学:在资源有限和不确定性面前,保证系统整体的稳定和公平,优先于保证单个数据包的送达。当洪水般的流量袭来时,与其让整个协议栈陷入泥潭导致系统无响应,不如果断丢弃一部分包,让TCP层通过重传机制(这又会体现在/proc/net/snmp的“RetransSegs”上)来自我恢复。
所以,这个计数器不仅仅是故障指示灯,它更是一个性能与资源平衡的仪表盘。偶尔的、零星的增长,或许是网络世界正常的“新陈代谢”。但当它开始持续、快速地攀升时,那就是内核在向你大声呼救,告诉你某个环节已经成为了木桶上最短的那块板。

这dropped计数害我熬了两宿,最后发现是conntrack满了 😩