在Linux系统中,TCP空闲连接超时是由应用层启用SO_KEEPALIVE后,内核参数net.ipv4.tcp_keepalive_time、tcp_keepalive_intvl以及tcp_keepalive_probes共同来控制的,这三者相互配合,缺一不可。要是没有调用setsockopt来启用SO_KEEPALIVE,那么就算修改内核参数也是没有效果的。

Linux里TCP空闲连接超时由谁控制?
Linux内核并不直接暴露一个全局“套接字空闲超时”配置项,net.ipv4.tcp_fin_timeout 控制的是 FIN_WAIT_2 状态的持续时间,不是空闲连接的保活超时;真正决定 TCP 连接空闲多久后被断开的,是 net.ipv4.tcp_keepalive_time 及其配套参数,但它们只对启用了 keepalive 的 socket 生效——也就是说,应用层没调用 setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)),这些内核参数就完全不起作用。
常见误区是以为改了 tcp_keepalive_time 就能让所有连接自动保活或断开,其实它只是“备选开关”,不是强制策略。
怎么查当前系统的 keepalive 参数值?
用 sysctl 查三个关键参数:
net.ipv4.tcp_keepalive_time:连接空闲多少秒后开始发送第一个 keepalive 探测包(默认 7200 秒,即 2 小时)
net.ipv4.tcp_keepalive_intvl:两次探测之间的间隔(默认 75 秒)
net.ipv4.tcp_keepalive_probes:连续探测失败多少次后判定连接已断(默认 9 次)
net.ipv4.tcp_keepalive_time:连接空闲多少秒后开始发送第一个 keepalive 探测包(默认 7200 秒,即 2 小时)net.ipv4.tcp_keepalive_intvl:两次探测之间的间隔(默认 75 秒)net.ipv4.tcp_keepalive_probes:连续探测失败多少次后判定连接已断(默认 9 次)执行命令查看:
sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes
注意:这些是系统级默认值,单个 socket 的实际行为还取决于应用是否显式启用 SO_KEEPALIVE,并可能通过 setsockopt 覆盖这些值(例如用 TCP_KEEPIDLE、TCP_KEEPINTVL、TCP_KEEPCNT)。
怎么查某个具体进程的 socket 当前 keepalive 设置?
Linux 不提供直接读取 socket 级 keepalive 参数的接口,但可通过 /proc/[pid]/fd/ + ss 或 lsof 辅助判断:
- 先用
lsof -i -n -p [pid] 找出目标 socket 的 fd 编号
- 再查该 fd 是否启用了 keepalive:
ss -tlnp | grep [pid] —— 如果输出中某行带 keepalive 字样(如 skmem:(r0,rb0,t0,tb0,f0,w0,o0,bl0,d0) 旁标注 keepalive),说明该 socket 已启用
- 更精确的方法是用
bpftrace 或 strace -e trace=setsockopt 启动进程,观察是否调用了 setsockopt(..., SOL_SOCKET, SO_KEEPALIVE, ...) 和后续的 TCP_* 选项
lsof -i -n -p [pid] 找出目标 socket 的 fd 编号ss -tlnp | grep [pid] —— 如果输出中某行带 keepalive 字样(如 skmem:(r0,rb0,t0,tb0,f0,w0,o0,bl0,d0) 旁标注 keepalive),说明该 socket 已启用bpftrace 或 strace -e trace=setsockopt 启动进程,观察是否调用了 setsockopt(..., SOL_SOCKET, SO_KEEPALIVE, ...) 和后续的 TCP_* 选项没有工具能直接 dump 出某个 socket 的 TCP_KEEPIDLE 实际值,因为内核不对外暴露这个运行时字段。
为什么改了 sysctl 却没生效?
- 应用在创建 socket 后、调用
connect() 或 accept() 前未启用 SO_KEEPALIVE,内核参数完全不参与
- 应用自己调用了
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, ...),覆盖了 sysctl 默认值,此时 sysctl 查到的只是“未被覆盖时的后备值”
- 某些服务(如 nginx、redis)默认禁用 keepalive,或仅对特定监听端口启用,需检查其配置文件里的
keepalive_timeout、tcp-keepalive 等字段
- 容器环境(如 Docker)中,容器网络命名空间可能继承宿主机 sysctl,也可能被 runtime 强制重置,需进容器内单独验证
connect() 或 accept() 前未启用 SO_KEEPALIVE,内核参数完全不参与setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, ...),覆盖了 sysctl 默认值,此时 sysctl 查到的只是“未被覆盖时的后备值”keepalive_timeout、tcp-keepalive 等字段空闲超时不是单一配置能决定的事,得从应用是否启用、内核参数是否匹配、运行时是否被覆盖三层去看。漏掉任意一层,查到的值都可能和实际行为对不上。