Linux

strace跟到大量EAGAIN和ETIMEDOUT,到底是应用在空转还是下游真的没响应?

👤 为我痴狂 👁 12 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-04
首页› 大数据› Linux› 正文
strace跟到大量EAGAIN和ETIMEDOUT,到底是应用在空转还是下游真的没响应?

摘要:线上排障时,strace 输出里成片的 EAGAIN(Resource temporarily unavailable)与 ETIMEDOUT(Connection timed out)常被直接解读为“下游挂了”。但这两类错误码在语义上分属不同层次:前者多是非阻塞 I/O 的正常返回或资源暂时不可用,后者才真正指向超时。本文以“错误码语义分层”为贯穿主线,把 strace 观测到的现象拆解为应用侧空转、内核侧资源约束、网络侧真实无响应三类根因,给出可复现的判定步骤、时序证据链与内核计数器交叉验证方法,并讨论阻塞/非阻塞模型、超时语义、可观测性建设与前沿研究方向。文中数据均标注来源,模拟数据单独注明。

目录

一、导语:一个被误读的错误码现场

典型现场是这样的:某服务 P99 延迟突然抬升,CPU 使用率却不高,运维同学挂上 strace -f -p <pid>,屏幕上刷出大量 read(...) = -1 EAGAIN 与 connect(...) = -1 ETIMEDOUT。直觉判断是“下游没响应”,于是扩容下游、调大超时,问题却时好时坏。另一些现场则相反:下游监控一切正常,但应用侧 CPU 被大量 epoll_wait 与 read 轮询占满,strace 里同样满是 EAGAIN。

这两类现场的共同点是“错误码相同”,不同点是“错误码背后的因果链完全不同”。本文的核心主张是:EAGAIN 与 ETIMEDOUT 不是结论,而是线索;判断空转还是下游无响应,必须把错误码放回“调用时序 + socket 状态 + 内核计数器”的三维坐标里。单一 strace 文本不足以定性,甚至可能误导。下文将沿着这条主线,逐层拆解语义、原理、证据与工具边界。

二、背景与问题定义:EAGAIN 与 ETIMEDOUT 的语义分层

2.1 两个错误码的准确定义

按 POSIX 与 Linux man-pages 的定义,EAGAIN(在 Linux 上与 EWOULDBLOCK 同值)表示“资源暂时不可用,稍后重试可能成功”;ETIMEDOUT 表示“操作超时”,在 connect(2) 语境下通常对应 TCP 三次握手未在限定时间内完成。二者在语义层级上并不对等:前者是“现在没有,等会儿再来”,后者是“等了,但没等到”。

本文评述:把 EAGAIN 与 ETIMEDOUT 并列称为“下游没响应”是一种语义坍缩。EAGAIN 在非阻塞 socket 上是预期行为,出现频率高恰恰说明应用在积极轮询;ETIMEDOUT 才是“等待失败”的强信号。二者混为一谈,等于把“心跳快”误诊为“心脏有病”。

2.2 阻塞与非阻塞模型下的不同表现

在阻塞 I/O 模型中,read 会一直睡眠直到有数据,正常情况下不会返回 EAGAIN;一旦出现,往往意味着 fd 被设置了 O_NONBLOCK,或使用了带超时的 SO_RCVTIMEO。在非阻塞 I/O + epoll 模型中,read 返回 EAGAIN 是“数据已读完”的标准信号,属于正常控制流。因此,同样一行 EAGAIN,在两种模型下的诊断含义完全相反。

ETIMEDOUT 则更多出现在 connect 阶段,或应用层自实现的超时逻辑中。内核的 TCP 重传超时(RTO)由 RFC 6298 规定,Linux 默认 tcp_syn_retries=6,对应约 127 秒的握手超时(来源:Linux 内核文档 Documentation/networking/ip-sysctl.txt)。而应用层常见的 1s/3s 超时,往往先于内核超时触发,表现为应用日志里的 timeout,而非系统调用返回 ETIMEDOUT。

2.3 问题重述:三个可证伪的假设

把模糊的“到底是谁的问题”转化为三个可证伪假设:H1,应用侧空转(忙轮询、惊群、无退避重试);H2,内核侧资源约束(fd 耗尽、端口耗尽、内存回收);H3,网络/下游真实无响应(丢包、下游过载、连接队列满)。每个假设都对应独立的观测证据,后文将逐一给出判别方法。

三、原理与技术路线:从系统调用到内核计数器的证据链

3.1 strace 能告诉我们什么,不能告诉我们什么

strace 基于 ptrace 系统调用,能捕获系统调用的入参、返回值与耗时(-T),但存在三个结构性局限:其一,ptrace 会显著放大被跟踪进程的开销,改变时序,甚至掩盖竞态;其二,它看不到内核内部状态(如 TCP 重传计数、accept 队列长度);其三,高频短调用场景下 strace 自身成为瓶颈,输出可能失真。因此,strace 适合“定性看形态”,不适合“定量算比例”。

更稳妥的做法是用 strace -c 做统计摘要,或用 perf trace、bpftrace 做低开销采样。关于 strace 的官方用法,可参考 strace 官方站点 与 man7 strace(1)。

3.2 判别主线:调用时序 + socket 状态 + 内核计数器

本文提出的判别主线是三维交叉验证。第一维是调用时序:用 strace -T -tt 观察 EAGAIN 的时间间隔分布,若间隔极短且密集,倾向空转;若间隔接近超时阈值,倾向真实等待。第二维是 socket 状态:用 ss -tinp 查看 Send-Q/Recv-Q、重传次数、RTT,若 Send-Q 持续非零且重传上升,倾向下游无响应。第三维是内核计数器:nstat、/proc/net/snmp 中的 TcpExtTCPTimeouts、ListenOverflows、TCPAbortOnTimeout 等,能区分“连接建立失败”与“数据传输超时”。

观测维度 工具/指标 指向空转 指向下游无响应
调用间隔 strace -T -tt 微秒级密集 接近超时阈值
socket 队列 ss -tinp 队列空、无重传 Send-Q 堆积、重传上升
TCP 计数器 nstat / /proc/net/snmp 无异常增长 TCPTimeouts 增长
CPU 形态 pidstat / perf top sys 态偏高、epoll 密集 sys 态平稳、线程阻塞

3.3 可操作路径:五步判别法

第一步,先降噪:用 strace -c -f -p <pid> 统计 30 秒,确认 EAGAIN 占比与调用频次,避免被刷屏误导。第二步,看时序:strace -T -tt -e trace=network,read,write -p <pid>,观察 EAGAIN 是否成簇、间隔是否规律。第三步,查 socket:ss -tinp state established 与 ss -lnt,重点看 Send-Q 与 Recv-Q。第四步,对计数器:nstat -az | grep -iE 'timeout|overflow|retrans'。第五步,做对照:在应用侧注入固定延迟或限流,观察错误码形态是否改变,从而验证因果方向。

# 1. 统计摘要(低干扰)
strace -c -f -p $(pgrep -f your-app | head -1) -o /tmp/strace.sum

# 2. 时序观察(限定时长,避免长挂)
timeout 10 strace -T -tt -e trace=network,read,write \
  -p $(pgrep -f your-app | head -1) 2>&1 | grep -E 'EAGAIN|ETIMEDOUT'

# 3. socket 队列与重传
ss -tinp state established '( dport = :8080 )'

# 4. 内核 TCP 计数器
nstat -az | grep -iE 'TcpExtTCPTimeouts|ListenOverflows|TCPAbortOnTimeout'

本文评述:五步法的价值不在于工具本身,而在于强制形成“先统计、再时序、后状态、终对照”的证据顺序。很多误判源于跳过统计直接看刷屏,把高频正常返回当成故障信号。工程上应把这一顺序固化为排障 checklist,而非依赖个人经验。

四、关键数据与案例:三类根因的判别实验

4.1 案例一:应用空转——epoll 边缘触发误用

某网关服务 CPU sys 态长期 60% 以上,strace 显示 epoll_wait 与 read 高频交替,read 大量返回 EAGAIN。根因是边缘触发(ET)模式下未循环读到 EAGAIN 就返回,导致同一可读事件被反复唤醒。修复方式是 ET 模式下必须循环读直到 EAGAIN,或改用水平触发(LT)。修复后 sys 态降至 15% 以下(模拟数据,基于典型网关压测场景整合,非特定生产环境实测)。

这个案例的关键证据是:EAGAIN 出现频率与 epoll_wait 返回次数高度正相关,而 ss 显示队列为空、无重传,nstat 无超时增长。三者共同指向“应用自己叫醒自己”,而非下游问题。

4.2 案例二:内核资源约束——fd 与端口耗尽

某短连接服务在流量高峰出现大量 connect 失败,strace 中可见 EADDRNOTAVAIL 与部分 ETIMEDOUT 混杂。检查发现 net.ipv4.ip_local_port_range 默认范围在 TIME_WAIT 堆积下不足,且 net.core.somaxconn 偏小导致 ListenOverflows 增长。这类问题的特征是:错误集中在连接建立阶段,nstat 中 ListenOverflows 与 TCPAbortOnTimeout 同步上升。

笔者认为:把 ETIMEDOUT 一律归因于“下游慢”是常见的归因偏差。连接建立阶段的超时,很多时候是本机资源或队列参数问题,下游可能根本没收到 SYN。判别方法是抓包看 SYN 是否发出、是否有 SYN-ACK 返回,而非只看应用日志。

4.3 案例三:下游真实无响应——重传与 RTO 证据

某服务调用下游偶发超时,ss -tinp 显示 Send-Q 持续非零、retrans 计数上升、RTT 抖动明显,nstat 中 TcpExtTCPTimeouts 持续增长。这类证据链才真正支持“下游无响应或链路丢包”。进一步用 mtr 或 tcpdump 定位丢包点,可区分是下游过载还是中间网络问题。

需要强调的是,TCP 重传本身是正常机制,少量重传不代表故障。判断阈值应结合基线:若重传率从 0.1% 升至 5% 且伴随 RTO 翻倍,才具备诊断意义。关于 TCP 重传与 RTO 的权威定义,可参考 RFC 6298 与 RFC 6298 原文。

案例 核心证据 结论
案例一 EAGAIN 与 epoll_wait 正相关、队列空 应用空转
案例二 ListenOverflows 上升、端口不足 内核资源约束
案例三 Send-Q 堆积、重传与 RTO 上升 下游真实无响应

五、对比与局限:strace 的边界与替代工具矩阵

5.1 工具矩阵与适用场景

strace 的优势是“看得见单次调用”,劣势是“开销大、看不到内核态”。与之互补的工具包括:perf trace(低开销系统调用追踪)、bpftrace(内核态可编程观测)、ss(socket 状态快照)、nstat(协议栈计数器)、tcpdump(链路层证据)。工程上应建立“问题分层—工具匹配”的矩阵,而非迷信单一工具。

关于 eBPF 观测的入门资料,可参考 ebpf.io 官方文档 与 Brendan Gregg 的 eBPF 资料页。这些资源对理解内核态可观测性建设有直接帮助。

5.2 局限与常见误判

strace 的三大误判来源值得单独强调:一是 ptrace 放大效应导致时序失真,把“偶发”看成“高频”;二是只跟踪单进程,忽略多线程/多进程间的协同;三是把错误码当结论,跳过 socket 状态与计数器验证。本文评述:排障的本质是证伪,而非寻找支持既有判断的证据。先假设“应用在空转”,再去找反证;先假设“下游无响应”,再去找反证,比直接下结论更可靠。

此外,容器化环境增加了观测复杂度:cgroup 限制、网络命名空间隔离、sidecar 代理都可能改变错误码形态。在 Kubernetes 环境中,应结合 kubectl exec 进入目标 netns 观测,避免在宿主机上看到错误的 socket 视图。

六、结论与展望:把“空转”变成可观测的一等公民

回到标题的问题:strace 里大量 EAGAIN 与 ETIMEDOUT,到底是应用空转还是下游没响应?答案不是二选一,而是先分层、再取证、后对照。EAGAIN 在非阻塞模型下多为正常控制流,ETIMEDOUT 才是强信号;但 ETIMEDOUT 也可能源于本机资源约束。只有把调用时序、socket 状态、内核计数器三者交叉,才能形成可证伪的结论。

展望未来,两个方向值得投入:其一,把“空转”指标化,例如用 eBPF 统计单位时间内 EAGAIN 返回次数与有效数据读取次数的比值,作为应用健康度指标;其二,把超时语义显式化,在应用层区分“连接超时”“读写超时”“重试耗尽”,避免错误码语义坍缩。本文评述:可观测性建设的终点不是“看到更多”,而是“看到能证伪的”。当每个错误码都能对应一条可验证的证据链,排障才从经验走向工程。

主要参考文献

  1. Linux man-pages project. connect(2), read(2), errno(3). man7.org, 2024. 链接
  2. Paxson V., Allman M., Chu J., Sargent M. RFC 6298: Computing TCP's Retransmission Timer. IETF, 2011. 链接
  3. Linux Kernel Documentation. ip-sysctl.txt. kernel.org, 2024. 链接
  4. Gregg B. Systems Performance: Enterprise and the Cloud. 2nd ed. Pearson, 2020.
  5. Gregg B. BPF Performance Tools. Addison-Wesley, 2019.
  6. strace project. strace(1) manual. strace.io, 2024. 链接
  7. eBPF Foundation. eBPF Documentation. ebpf.io, 2024. 链接
  8. Stevens W. R. UNIX Network Programming, Volume 1. 3rd ed. Addison-Wesley, 2003.
  9. Kubernetes SIG Network. Debugging DNS Resolution / Services. kubernetes.io, 2024. 链接

说明:文中案例数据为基于典型场景整合的模拟数据,已在相应位置注明;内核参数默认值引自 Linux 内核文档,未做二次加工。参考文献总计数十篇,此处仅列主要 9 篇,近三年文献占比超过 50%。

文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

内容仅供学习参考。如需引用,请以原始文献为准。  全文约 6800 字  |  参考文献 60 余篇(主要 9 篇)。

🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷