进程挂起在epoll_wait是正常现象,表示线程正阻塞等待网络事件;需结合-T/-tt参数看耗时、检查socket状态及信号干扰,排除真卡死。

strace -p 卡在 epoll_wait 是正常现象
进程挂起在 epoll_wait 本身不表示异常,而是事件驱动模型的典型行为:线程正阻塞等待网络事件(如新连接、数据到达、对端关闭)。strace -p 只是如实反映当前系统调用状态,并非 strace 导致卡住。
常见误判场景包括:误以为进程“假死”,或怀疑 strace 干扰了业务逻辑。实际上,只要进程没被信号中断或崩溃,它就在合法等待中。
- 确认是否真卡死:用
kill -0 $PID检查进程是否存活;用ps -o pid,ppid,stat,time,comm -p $PID看STAT字段是否为S(可中断睡眠)而非D(不可中断,需警惕) - 若进程长期无响应且
epoll_wait不返回,优先排查上游连接方是否断连未通知、TCP keepalive 是否启用、或对端静默丢包 -
strace -p本身会轻微增加调度开销,但不会阻止epoll_wait被唤醒 —— 唤醒由内核网络子系统触发,与 trace 工具无关
用 -T 和 -tt 看 epoll_wait 的实际等待时长
默认 strace -p 不显示系统调用耗时,无法判断是毫秒级常规等待,还是已卡住数分钟。加时间戳参数才能定位真实延迟。
- 用
strace -p $PID -T:在每行末尾显示该次系统调用实际耗时(单位秒),例如epoll_wait(5, [], 128, -1) = 0表示只等了 12 微秒 - 用
strace -p $PID -tt:打印微秒级绝对时间戳,方便比对前后调用间隔,识别是否某次epoll_wait返回后长时间没下一次调用(说明业务逻辑卡在用户态) - 避免只看
-t(秒级时间戳),精度不够,无法区分“正常轮询”和“异常挂起”
结合 ss 和 /proc/$PID/fd 确认 epoll 实例关联的 socket 状态
epoll_wait 在等什么?不能只看系统调用名,得知道它监听的 fd 对应哪些 socket,以及这些 socket 当前是否健康。
- 先用
ls -l /proc/$PID/fd/ | grep epoll找出 epoll fd(通常是数字,如3) - 再用
cat /proc/$PID/fdinfo/3查看该 epoll 实例注册了哪些被监听 fd(字段tfds列出所有注册项) - 对每个被监听 fd(比如
7),执行ss -tanfp "fd==7"或ls -l /proc/$PID/fd/7看它指向的 socket 类型、对端地址、TCP 状态(ESTAB/CLOSE_WAIT/TIME_WAIT) - 特别注意
CLOSE_WAIT过多:说明对端已关闭,但本端应用没调用close()或read()到 EOF,导致 socket 一直留在 epoll 中等待读事件,而epoll_wait会持续返回就绪 —— 此时你会看到高频epoll_wait返回 +read()返回 0,不是卡住,是业务没处理完关闭流程
epoll_wait 返回 -1 EINTR 时容易被忽略的信号干扰
如果 strace -p 输出中频繁出现 epoll_wait(... ) = -1 EINTR (Interrupted system call),说明有信号反复打断等待。这不是卡住,但可能掩盖真正问题。
- 常见诱因:
strace自身发送SIGSTOP/SIGCONT(尤其在多线程进程上)、定时器信号(ITIMER_REAL)、或外部kill -USR1类调试信号 -
EINTR下,epoll_wait会立即返回,不等待事件,导致 CPU 空转(busy loop),表现像“卡在日志里刷屏”,实则是高频重试 - 检查信号屏蔽:用
cat /proc/$PID/status | grep Sig看SigBlk(阻塞信号集)和SigCgt(捕获信号集),确认是否有未预期信号被进程注册处理 - 临时规避:用
strace -p $PID -e trace=epoll_wait,read,write减少 trace 干扰;长期方案是业务代码对EINTR做正确重试或屏蔽无关信号
真正难排查的是 epoll_wait 长期不返回且无超时(timeout = -1),同时 socket 状态全为 ESTAB、无错误日志、CPU 使用率极低 —— 这时候得怀疑内核协议栈异常、网卡 offload 故障,或者进程被 cgroup 冻结、OOM killer 标记但尚未 kill。










