Appearance
网络性能指标 —— 丢包、重传、连接状态、收发吞吐量
sar -n DEV 1看到rxdrop/s不为 0 是怎么回事?netstat -s里segments retransmitted涨了意味着什么?TIME_WAIT 堆积 30000 个会不会有问题?ifconfig的errors和dropped区别在哪?本文拆解网络层的核心指标,覆盖从网卡到 TCP 协议栈的完整统计链路。
更新时间:2026-08-10
一、网络指标分层模型
网络指标分三层,数据源不同,排查入口也不同:

Fig 1.1:网络指标的五层模型——不是 OSI 七层标准,也不是 TCP/IP 四层经典分法,而是面向性能排查的指标分层模型。排查网卡丢包看
/proc/net/dev,排查 TCP 重传说看/proc/net/snmp。
1.1 这个分层是不是"网络架构"
不是经典的网络协议栈模型,而是一个面向性能排查的指标分层框架。它的划分依据不是协议功能边界,而是每个包在 Linux 内核中实际经过的统计点——一个包从网卡到应用程序,在每一层都有对应的计数器和观测接口。理解这个分层,你就知道每个网络指标的数字是在哪里记录的、由谁记录的、以及丢包发生在哪一层。
与经典模型的对照:
| 对比维度 | TCP/IP 四层模型 | Linux 网络指标五层模型 |
|---|---|---|
| 划分依据 | 协议功能(应用层/传输层/网络层/链路层) | 数据包在内核中的统计经过点 |
| 关注重点 | 协议交互、封装/解封装 | 丢包归因、延迟来源、吞吐瓶颈 |
| 应用层 | HTTP/DNS/应用程序 | socket 层:读写缓冲区满、连接建立/关闭 |
| 传输层 | TCP/UDP | 协议层:重传(RTO/TLP)、RST、checksum 错误 |
| 网络层 | IP | IP 层:路由决策、分片、IPsec |
| 链路层/物理层 | 驱动 + 网卡 | 合并为两层:ring buffer 丢包 vs 物理层 CRC 错误 |
1.2 每层如何工作、指标如何统计
应用层(socket 层)
包在这层做什么:应用程序通过 read()/write() 或 send()/recv() 在 socket fd 上收发数据。socket 在内核中维护独立的发送缓冲区和接收缓冲区——这是用户态和内核态之间的数据边界。应用程序写入 send buffer,内核协议栈从 send buffer 取出数据逐层向下发送;网卡收到包后逐层向上递交,最终放入 recv buffer,应用程序通过 read() 取出。
这层的指标怎么来的:socket 层的统计是内核在 socket 状态机切换时由协议栈代码直接递增计数器。例如每建立一个 TCP 连接,/proc/net/snmp 中的 ActiveOpens(主动连接)或 PassiveOpens(被动连接)就 +1;每次 socket 因接收缓冲区满而丢弃到达的数据,/proc/net/snmp 中的 RcvbufErrors(UDP)就 +1。ss -s 的 estab/timewait/close-wait 等连接数统计,则是内核在遍历所有 socket 的 tcp_hashinfo 时聚合计算的结果——它反映的是瞬时快照而非累计值。
关键工具:ss -s、ss -ti(单连接详情)、/proc/net/sockstat(socket 内存使用量)。
传输层(TCP/UDP)
包在这层做什么:负责端到端的可靠传输。TCP 的核心工作包括:三次握手建立连接、拥塞控制(CUBIC/BBR 算法动态调整 cwnd)、超时重传(RTO timer 触发)、快速重传(收到 3 个 dup ACK)、流控(滑动窗口通告)。UDP 则几乎不做额外处理——只管发包,不保证到达。
这层的指标怎么来的:传输层指标来自两条统计路径。全局累计计数器(/proc/net/snmp 和 /proc/net/netstat):内核在 TCP/UDP 协议栈的关键事件点维护全局 atomic_t 计数器。例如 TCP 发送一个段后发现需要重传,RetransSegs 原子递增;收到一个乱序段,OutOfOrder 递增。这些是系统级别的累计值,开机至今持续累加。单连接信息(ss -ti):内核为每个 TCP socket 维护 tcp_sock 结构体,其中记录了这个连接的 retrans(重传次数)、rtt(往返时间)、cwnd(拥塞窗口)等字段。ss -ti 读取的是 tcp_sock 的实时快照。
关键工具:cat /proc/net/snmp | grep Tcp(全局累计)、cat /proc/net/netstat(扩展 TCP 统计)、ss -ti(单连接实时信息)、nstat -az(增量模式)。
IP 层
包在这层做什么:接收传输层下来的数据段,添加 IP 头(源/目的 IP、TTL、协议号),查找路由表决定从哪个网卡发出,必要时进行分片(如果包大小超过路径 MTU)。接收方向则执行反向操作:校验和验证、分片重组、根据协议号(TCP=6, UDP=17)投递给对应的传输层处理函数。
这层的指标怎么来的:IP 层的统计也记录在 /proc/net/snmp 中(Ip 开头的行),包括 InReceives(收到的 IP 包总数)、InDelivers(成功投递给传输层的包数)、OutRequests(请求发送的 IP 包数)、ReasmFails(分片重组失败数)、InDiscards(被丢弃的 IP 包数)等。每个计数器对应内核 net/ipv4/ip_input.c 和 net/ipv4/ip_output.c 中的具体代码路径——例如 InDiscards 是在 ip_rcv_finish() 发现路由查询失败时递增的。
关键工具:cat /proc/net/snmp | grep "^Ip:"、与传输层统计交叉验证(IP 层丢包 + 传输层重传 = 完整的网络问题画像)。
网卡驱动层
包在这层做什么:驱动维护 ring buffer(环形缓冲区)——一个固定大小的共享内存区域,网卡通过 DMA(Direct Memory Access,直接内存访问)直接将收到的包写入 ring buffer 中的 descriptor,然后通过中断通知内核。内核的 NAPI 机制在中断触发后切换到轮询模式(软中断 NET_RX),从 ring buffer 批量取出数据包,开始向上递交到协议栈。发送方向对称:驱动将待发送的包放入 TX ring buffer,网卡通过 DMA 读取并发送。
这层的指标怎么来的:/proc/net/dev 的统计数据来自内核 net/core/net-procfs.c,它遍历每个网络设备的 net_device 结构体中的 rtnl_link_stats64 字段。网卡驱动在每次收发包时更新这个结构体——收到一个包,rx_packets++、rx_bytes += len;ring buffer 满了无法接收新包,rx_dropped++;CRC 校验失败,rx_errors++。ethtool -S 则直接读取网卡硬件寄存器,提供比 /proc/net/dev 更细粒度的硬件层统计(如不同队列的丢包数、DMA 映射失败次数等)。两者的关系是:/proc/net/dev 是内核侧的软件统计,ethtool -S 是网卡侧的硬件统计,交叉对照可以确定丢包是在驱动层还是硬件层发生的。
关键工具:sar -n DEV 1(实时速率)、sar -n EDEV 1(实时错误/丢包)、cat /proc/net/dev(累计值)、ethtool -S eth0(硬件寄存器级统计)、ethtool -g eth0(ring buffer 当前大小)。
物理网卡层
包在这层做什么:物理网卡负责将数字信号转换为物理介质上的电信号/光信号(发送),或反之(接收)。这一层涉及 MAC 地址过滤、VLAN tag 处理、硬件 checksum offload、TSO/GRO 等硬件加速特性。物理层的错误通常是硬件故障或物理链路问题导致。
这层的指标怎么来的:物理层的统计完全来自网卡硬件寄存器,由驱动通过 ethtool -S 暴露。常见的 FPGA/ASIC 级别计数器包括:rx_crc_errors(CRC 校验失败——通常意味着网线质量差或电磁干扰)、rx_frame_errors(帧对齐错误)、rx_length_errors(帧长度不合法)、tx_fifo_errors(发送 FIFO 溢出)、rx_missed_errors(网卡内部缓冲区不足导致丢包)。这些指标在内核层面不可见——即使 /proc/net/dev 的 rx_dropped=0,物理层的 rx_missed_errors 仍然可能在增长。因此排查硬件级丢包必须使用 ethtool -S。
关键工具:ethtool -S eth0 | grep -i error、ethtool -S eth0 | grep -i drop、ethtool eth0(链路状态、速率协商)。
1.3 五层指标定位对照表
| 层次 | 数据包状态 | 典型丢包原因 | 观测工具 | 统计来源 |
|---|---|---|---|---|
| 应用层 | 在 socket 缓冲区中 | recv buffer 满(UDP) | ss -s、/proc/net/snmp | socket 状态机计数器 |
| 传输层 | TCP/UDP 协议处理中 | 超时重传、checksum 错 | /proc/net/snmp、ss -ti | tcp_sock 结构体 + 全局 atomic 计数器 |
| IP 层 | 路由/分片处理中 | 路由不可达、分片重组失败 | /proc/net/snmp(Ip 行) | ip_input.c / ip_output.c 中计数器 |
| 网卡驱动层 | ring buffer 中 | ring buffer 满 | sar -n EDEV、/proc/net/dev | net_device->rtnl_link_stats64 |
| 物理网卡 | 硬件 FIFO / PHY | CRC 错误、FIFO 溢出 | ethtool -S eth0 | 网卡硬件寄存器 |
二、网卡层指标 —— /proc/net/dev
bash
$ cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop|bytes packets errs drop
eth0: 123456 12345 0 0 543210 43210 0 0| 列名 | 含义 | 对应 sar -n DEV 列 | 严重性 |
|---|---|---|---|
bytes | 收发字节数(含协议头) | rxkB/s、txkB/s | 正常指标 |
packets | 收发包数 | rxpck/s、txpck/s | 正常指标 |
errs(RX) | 接收错误(CRC 校验失败、帧对齐错误等物理层错误) | rxerr/s | 严重:网线/光纤/硬件 |
drop(RX) | 接收丢包(ring buffer 满了,网卡无法把包交给内核) | rxdrop/s | 严重:软件接收瓶颈 |
errs(TX) | 发送错误(载波丢失、冲突过多等) | txerr/s | 严重:硬件/链路问题 |
drop(TX) | 发送丢包(发送队列满了) | txdrop/s | 严重:发送过快 |
2.1 RX drop(接收丢包)——最需要关注的指标
根因:网卡收到包后放入 ring buffer(环形缓冲区),内核通过 NAPI(New API,中断+轮询混合机制)从 ring buffer 取包。如果内核取包速度赶不上网卡收包速度,ring buffer 满了,后续的包直接丢弃——这个丢包是静默的,TCP 会通过重传来恢复,但延迟暴增。
排查:
bash
# 1. 确认丢包
sar -n EDEV 1 # 看 rxdropped/s
# 2. 看当前 ring buffer 大小
ethtool -g eth0
# 3. 增大 ring buffer(如果支持)
ethtool -G eth0 rx 4096
# 4. 确认是否是特定 CPU 的软中断瓶颈
watch -d -n1 cat /proc/softirqs | grep NET_RX2.2 TX drop(发送丢包)
根因通常是发送队列(TX ring buffer)满了——应用发送速度超过网卡/网络的处理能力,或者流控(BQL,Byte Queue Limits)在限制发送。
三、协议层指标 —— /proc/net/snmp 和 /proc/net/netstat
3.1 TCP 重传统计——最敏感的延迟指标
bash
$ cat /proc/net/snmp | grep Tcp
Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets ...
Tcp: 1 200 120000 -1 12345 23456 100 150 ...| 字段 | 含义 | 危险信号 |
|---|---|---|
RetransSegs | TCP 重传的报文段总数(累计值) | 持续增长 → 网络有丢包或延迟异常 |
EstabResets | 已建立的连接被 RST 重置 | 高 → 服务端/中间设备在断开连接 |
TCPTimeouts(来自 netstat) | TCP 超时次数 | 持续增长 → RTO 被触发,对端不响应 |
TCPLossProbes(来自 netstat) | TLP(Tail Loss Probe,尾部丢失探测)探测次数 | 高 → 存在尾部丢包 |
TCPSpuriousRtxHostQueues | 伪重传(因乱序而非丢包触发) | 高 → 网络存在严重乱序 |
3.2 重传 vs 乱序——别弄混
- 重传(RetransSegs):发了包但没收到 ACK,RTO 超时后重新发送
- 乱序(OutOfOrder):包到达了但顺序不对,TCP 缓冲等待重排
两者都会导致延迟增加,但乱序不涉及重传,对带宽的影响较小。可以用 ss -ti 看每个连接的重传统计。
3.3 用 ss -ti 看单连接 TCP 信息
bash
$ ss -ti state established
...
tcp ESTAB 0 0 192.168.1.1:22 192.168.1.2:54321
cubic wscale:7,7 rto:200 rtt:0.345/0.123 mss:1448
pmtu:1500 rcvmss:536 advmss:1448 cwnd:10
bytes_acked:123456 bytes_received:654321
segs_out:1000 segs_in:800
retrans:0/5 # 快速重传 0 次 / 超时重传 5 次 ← 关键
...| 字段 | 含义 |
|---|---|
retrans X/Y | X=快速重传次数,Y=超时重传次数。超时重传 > 0 且持续增长 → 网络问题严重 |
rtt | 当前 RTT / 平均 RTT(ms) |
rto | 当前重传超时(ms) |
cwnd | 拥塞窗口大小(MSS 为单位) |
unacked | 已发送未确认的报文数 |
四、连接状态指标
bash
$ ss -s
Total: 500
TCP: 300 (estab 50, closed 100, orphaned 5, timewait 120)| 状态 | 含义 | 危险信号 |
|---|---|---|
estab | ESTABLISHED,正常活动连接 | — |
timewait | TIME_WAIT,主动关闭方等待 2MSL | > 30000 可能影响本地端口可用性(通过 tcp_tw_reuse 缓解) |
close-wait | CLOSE_WAIT,对端已关闭但本端未调用 close | 任何数值持续存在 → 应用层 bug(忘记关连接) |
syn-sent | 正在发起连接中 | 大量积压 → 对端不可达或防火墙 drop SYN |
syn-recv | 收到 SYN 但未完成三次握手 | 大量积压 → SYN flood 攻击或半连接队列太小 |
orphaned | 孤儿 socket(无对应 fd 但还在传输数据) | 大量 → fd 泄漏或应用过早关闭连接 |
五、UDP 丢包
bash
$ cat /proc/net/snmp | grep Udp
Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors| 字段 | 含义 | 危险信号 |
|---|---|---|
RcvbufErrors | 因 socket 接收缓冲区满而丢包 | > 0 → 增大 net.core.rmem_max 或应用处理太慢 |
SndbufErrors | 因 socket 发送缓冲区满而丢包 | > 0 → 增大 net.core.wmem_max 或发送速率超限 |
InErrors | 非 NoPorts 的其他接收错误(校验和等) | > 0 → 链路质量问题 |
UDP 丢包是静默的——应用层不会收到任何通知。排查 UDP 丢包必须看
/proc/net/snmp,不能依赖应用日志。
六、常见排查模式速查
| 现象 | 工具 | 关注指标 | 排查方向 |
|---|---|---|---|
| 网络延迟突增 | ss -ti | retrans、rtt | 重传 > 0 → 丢包;RTT 突增 → 网络路径问题 |
| 吞吐量上不去 | sar -n DEV 1;ethtool -g eth0 | rxkB/s vs 预期带宽、ring buffer 大小 | ring buffer 太小、网卡速率协商、CPU 瓶颈 |
| rx 丢包 | sar -n EDEV 1 | rxdrop/s | 增大 ring buffer、RSS 分散软中断、net.core.netdev_budget |
| TIME_WAIT 堆积 | ss -s | timewait | 调 tcp_tw_reuse=1、调整应用为被动关闭 |
| CLOSE_WAIT 不消失 | ss -tan state close-wait | 连接数 | 应用 bug:收到 FIN 后没调 close() |
| 重传严重 | cat /proc/net/snmp;ss -ti | RetransSegs、单连接 retrans | ping -c 100 看丢包率、mtr 看哪一跳丢包 |
七、一句话总结
网络指标从网卡层到协议层形成完整统计链路——rxdrop/s 代表内核取包赶不上网卡收包速度(ring buffer 瓶颈),RetransSegs(TCP 重传)是最敏感的端到端延迟指标,它的增长意味着网络存在丢包或严重延迟;ss -ti 的单连接 retrans 和 rtt 是精确定位"哪个连接出问题"的关键;UDP 丢包是静默的,只能通过 /proc/net/snmp 发现。