游戏卡顿白皮书 › L7 服务器操作系统(内核)
内核 socket 缓冲区不足 Small socket buffers
原因 ID so-sockbuf · 主责 运维团队·系统运维 · 配合 研发团队·服务器开发
在含图示和实验的完整版中打开此卡片 →
收发缓冲区太小时,一旦突发流量涌来,UDP 收到的数据包会被丢弃,TCP 发送则因缓冲区没有余量而阻塞。
起因 SO_SNDBUF、SO_RCVBUF 用的是默认值或设得太小 → 结果 突发流量或接收线程短暂停住期间,UDP 接收缓冲区溢出而丢包;TCP 发送缓冲区没有余量,只能等待 → 画面表现 瞬移(UDP 丢包)或快进(TCP 等待)
- 症状
- 瞬移, 快进
- 因素
- 丢包, 停顿
- 谁会遇到
- 全服
- 何时出现
- 人多的时候
- 负责方
- 主责 运维团队·系统运维 · 配合 研发团队·服务器开发
- 研发团队要做的事
- 在代码中按流量设置缓冲区大小(SO_SNDBUF、SO_RCVBUF);注意 TCP 手动指定大小后会关闭 Linux 的自动调节;设得太大会让旧数据堆在缓冲区里、延迟变大,要适度;不要让接收线程停住。
- 运维团队要做的事
- 调整内核上限(rmem_max、wmem_max,代码里设置的缓冲区大小也不能超过这个值)和默认值(rmem_default);监控缓冲区溢出计数器(RcvbufErrors)。
- 数值参考
- Linux UDP 接收缓冲区默认约 208 KB。即使是小数据包,每个包占用的内核内存也远大于实际大小,几十到几百个就会填满。每秒接收 10 万个包的服务器,接收线程只要停住几 ms 就会溢出。
- 监控图上
- 偶发随机尖峰 · UDP 接收缓冲区溢出(UdpRcvbufErrors)
- 查看位置
- 看 nstat -az 的 UdpRcvbufErrors 增量和 ss -uamn 的 skmem(rb 为接收缓冲区大小,d 为没能放进 socket 而丢弃的包数);TCP 看 ss -tm 的 skmem 中发送排队内存(w)是否达到发送缓冲区大小(tb)
- 确认依据
- 突发流量或接收线程停住的时刻 UdpRcvbufErrors(或 socket 的 d)增加,rb 在默认值(约 208 KB)附近。TCP 则是 w 贴着 tb,send 阻塞
- 排除依据
- 计数器没有变化却有丢包,查网卡层(“环形缓冲区不足”)或网络链路
- 确认手段
- 运维工具即可确认(无需游戏代码)
出处
- socket(7) — Linux manual page Linux man-pages
SO_RCVBUF、SO_SNDBUF 默认值为 rmem_default、wmem_default,上限为 rmem_max、wmem_max;设置的值会被内核加倍 - include/net/sock.h (Linux v6.18) Linux kernel
默认 socket 缓冲区定义为 256 个 256 字节包(含 sk_buff 开销)的大小(SKB_TRUESIZE(256)×256);小帧也按 sk_buff+MTU 计算(约 208 KB 是 x86-64 下的计算值) - IP Sysctl Linux kernel
tcp_rmem、tcp_wmem:直接设置 SO_RCVBUF、SO_SNDBUF 后,该 socket 的自动调节会关闭 - net/ipv4/udp.c (Linux v6.12) Linux kernel
UDP 接收队列超过 socket 缓冲区大小时立即丢弃,并增加 RcvbufErrors - net/ipv4/proc.c (Linux v6.12) Linux kernel
nstat 显示的计数器名:Udp 组的 RcvbufErrors、SndbufErrors - ss(8) — Linux manual page iproute2
-m 的 skmem:rb 接收缓冲区大小,tb 发送缓冲区大小,w 发送排队内存,d 放入 socket 之前就丢弃的包数
相关原因
同一层:L7 服务器操作系统(内核)
其他层中同样导致“瞬移”的原因
查看含图示和实验的原卡片