Linux TCP 内存模型与 tcp_mem 三阈值:系统级防 OOM 的底线
Linux TCP 内存模型与 tcp_mem 三阈值:系统级防 OOM 的底线

在支撑数十万乃至数百万长连接的高并发网关(如 WebSocket 推送集群、IM 消息中枢、分布式 API 网关)运维实战中,很多团队都曾遭遇过下面这场惊心动魄的系统级雪崩:
服务器物理硬件配置了 128GB 内存,随着长连接数逐步攀升至 80 万,free -m 显示可用内存急剧缩减,随后内核突然频繁触发 OOM Killer,核心网关进程被操作系统以 SIGKILL 强行终止。登录终端查看内核日志 dmesg,控制台赫然记录着大量致命告警:TCP: out of memory -- consider tuning tcp_mem!
许多工程师十分不解:“我们在应用层为每个连接设置的读写缓冲区明明只有几 KB,为什么几十万长连接就能吃光上百 GB 物理内存,甚至越过操作系统防线引发 OOM?”
这背后的根源,正是 Linux 内核网络子系统的 TCP 内存模型(TCP Memory Accounting) 以及系统的全局总控闸门——net.ipv4.tcp_mem 三阈值机制。本文深入内核协议栈源码与物理账本,拆解长连接下的内存黑盒与生产级防 OOM 调优底线。
单条 TCP 连接的真实内核物理账本
在 Linux 内核中,一条活跃的 TCP 连接绝不仅仅是用户态所持有的一个整数文件描述符(FD)。它在内核空间由一系列精密的数据结构和缓冲区共同维系:
单条 TCP 连接内核物理内存开销拆解:
┌────────────────────────────────────────────────────────────┐
│ 1. struct sock + struct tcp_sock 内核控制块 : 约 2.5 ~ 3.2 KB (硬常驻) │
├────────────────────────────────────────────────────────────┤
│ 2. sk_buff (skb) 元数据结构体 : 每个数据包约占 256 ~ 512 字节 │
├────────────────────────────────────────────────────────────┤
│ 3. 接收缓冲区 (Receive Queue) : 保存已接收但应用尚未 read 的数据 │
├────────────────────────────────────────────────────────────┤
│ 4. 发送缓冲区 (Send Queue) : 保存已发送但尚未收到对端 ACK 的数据 │
├────────────────────────────────────────────────────────────┤
│ 5. 乱序队列 (Out-of-Order Queue) : 保存网络抖动时乱序到达的报文 │
└────────────────────────────────────────────────────────────┘
真实物理账本测算:
- 空闲静默期:当连接建立后无数据收发,仅维持心跳时,单个 Socket 占用的内核内存基线约为 3.5 KB 到 4 KB;
- 活跃波动期:当网络出现轻微抖动、丢包或下游客户端处理变慢时,内核的自动缓冲区调优机制(DRS,Dynamic Right-Sizing)会自动扩容接收与发送缓冲区,单连接内存消耗会迅速膨胀至 32 KB、64 KB 甚至 128 KB;
- 百万连接的乘法效应:
对于 100 万并发长连接,即便按平均单连接 64 KB 的保守值计算:
$$1,000,000 \times 64\text{ KB} \approx 64\text{ GB 纯内核 Slab/页框内存!}$$
再加上 Linux 内核伙伴系统(Buddy System)与 SLUB 分配器的页级内存对齐碎片,一台 128GB 的物理机可用内存将被瞬间吞噬殆尽!
net.ipv4.tcp_mem 三阈值的内核调控状态机
为了防止不可控的 TCP 流量把整机内存拖垮,Linux 内核设立了基于 物理内存页(Page,x86_64 架构下 1 页 = 4096 字节) 的全局三级警戒水位线——net.ipv4.tcp_mem:
# 查看当前系统的 tcp_mem 三阈值 (单位: 4KB 内存页)
cat /proc/sys/net/ipv4/tcp_mem
# 常见默认输出示例: min (185000) pressure (246000) max (369000)
tcp_mem 三级水位状态机流转模型:
0 ──────────────── [ min (安全区) ] ──────────────── [ pressure (承压警戒线) ] ──────────────── [ max (绝对上限) ] ──>
│ │ │
▼ ▼ ▼
【绿色安全区: 自由生长】 【黄色承压区: 强制内存抑制】 【红色崩溃区: 强制丢包丢连接】
- TCP 内存页 < min - TCP 内存页介于 pressure 与 max 之间 - TCP 内存页 >= max
- 内核不干预 Socket 缓冲区自适应扩容 - 内核置全局标志 tcp_memory_pressure = 1 - 内核立刻拒绝分配任何新 TCP 内存
- 优先保障高吞吐与大带宽数据流 - 严禁任何 Socket 缓冲区继续膨胀 - 强行丢弃新到达的报文与 SYN 握手
- 强制压低接收窗口,抑制对端发包 - 控制台疯狂抛出 "TCP: out of memory"
1. min(最小保障水位)
当全系统所有 TCP 连接消耗的内存页总和低于 min 时,内核认为网络内存非常充裕,绝不施加任何限制,允许各个 Socket 根据网络带宽延迟积(BDP)自由扩容缓冲区。
2. pressure(承压调节水位)
当 TCP 总内存突破 pressure 阈值时,内核将全局变量 tcp_memory_pressure 置为 1,系统正式进入承压状态:
- 禁止扩容:所有 Socket 严禁申请新的缓冲内存;
- 主动抑制:内核在回包时强制将 TCP Window 缩小,迫使对端发送方降低发包速率;
- 硬性回收:尝试压缩已分配但未使用的 skb 空间,全力把内存压回
min水位线以下。
3. max(绝对硬性天花板)
当 TCP 总内存强行冲破 max 水位时,内核触发最后的自我防御机制:
- 拒绝分配:所有针对
sk_buff的内存申请直接返回失败; - 连接掐断与丢包:新到达的数据包被直接丢弃,新的 TCP 三次握手被直接发送 RST 掐断,网关服务陷入大面积瘫痪。
百万长连接网关的生产黄金调优配置
针对 128GB 内存的高并发长连接网关,必须通过 /etc/sysctl.conf 对内核全局配额与单连接缓冲进行精细化重构:
# /etc/sysctl.conf 针对 128GB 物理内存服务器的百万连接生产优化
# 1. 扩容 tcp_mem 全局配额 (单位为 4KB 内存页)
# 分别分配: min=12GB(3145728页), pressure=48GB(12582912页), max=80GB(20971520页)
net.ipv4.tcp_mem = 3145728 12582912 20971520
# 2. 严格收紧单个 Socket 的读写缓冲区配额 (防止少数异常连接无限吃内存)
# 格式: min default max (单位: 字节)
# 将默认读缓冲区降至 8KB, 最大硬上限限制在 64KB
net.ipv4.tcp_rmem = 4096 8192 65536
# 将默认写缓冲区降至 8KB, 最大硬上限限制在 64KB
net.ipv4.tcp_wmem = 4096 8192 65536
# 3. 调优连接队列与系统全局文件描述符
fs.file-max = 2097152
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# 4. 关闭慢速连接的 TCP 缓冲区自适应过度膨胀
net.ipv4.tcp_moderate_rcvbuf = 1
监控指标与预警看板
在生产监控中,必须通过 /proc/net/sockstat 建立秒级网络内核内存监控:
# 查询实时 Socket 内存统计
cat /proc/net/sockstat
输出内容解析:
sockets: used 865200
TCP: inuse 852100 orphan 150 tw 3200 alloc 852300 mem 12850400
TCP: inuse:当前系统中活跃承载的 TCP 连接数;TCP: orphan:孤儿连接数(已在应用层 close 但内核尚未完成挥手流程的连接);TCP: mem:当前所有 TCP 连接消耗的物理内存页数($12850400 \times 4\text{ KB} \approx 50.19\text{ GB}$);- 生产告警阈值:当
TCP: mem达到tcp_mem的pressure阈值 80% 时,必须立即触发 P0 级扩容或网关入口限流告警。
总结
操作系统的内存不是取之不尽的抽象符号,每一条长连接都在真实地消耗着内核底层的物理页框。搞清楚单连接的物理开销构成,读懂 tcp_mem 的三级调控逻辑,并用精细的单连接缓冲上限守住整机内存红线,才能在百万长连接的惊涛骇浪中,筑起坚不可摧的系统级防 OOM 钢铁防线。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)