
当 go 服务因死锁、竞态或调度阻塞而无响应时,可通过发送 sigquit 信号触发运行时打印完整 goroutine 堆栈,无需修改代码即可快速定位卡死位置。
当 go 服务因死锁、竞态或调度阻塞而无响应时,可通过发送 sigquit 信号触发运行时打印完整 goroutine 堆栈,无需修改代码即可快速定位卡死位置。
Go 运行时内置了强大的诊断机制,无需侵入式修改代码即可在进程挂起时获取全局状态快照。最直接有效的方式是向目标进程发送 SIGQUIT 信号:
# 查找进程 PID(例如你的 server) ps aux | grep myserver # 向 PID 发送 SIGQUIT(等价于 Ctrl+\) kill -SIGQUIT <PID>
执行后,Go 运行时会立即将所有 goroutine 的当前调用栈(包括正在运行、休眠、等待 channel 或锁的 goroutine)以可读格式打印到标准错误(stderr),例如:
SIGQUIT: quit
PC=0x1059b7f m=0 sigcode=0
goroutine 0 [idle]:
runtime.park(0x108e320, 0x1156a60, 0xc00004a748)
/usr/local/go/src/runtime/proc.go:367 +0x13d
runtime.gopark(0xc00004a748, 0xc00004a738, 0x14, 0x1, 0x0)
/usr/local/go/src/runtime/proc.go:372 +0x44
...
goroutine 1 [chan receive]:
main.main()
/path/to/main.go:25 +0x1a5
goroutine 2 [force gc (idle)]:
runtime.gopark(0x108e320, 0x1156a60, 0x14, 0x1, 0x0)
/usr/local/go/src/runtime/proc.go:372 +0x44
...该输出能清晰揭示:哪些 goroutine 在阻塞(如 chan receive、semacquire、select)、是否发生死锁、是否存在大量堆积的 goroutine,以及主 goroutine 是否卡在某处——这对排查你描述的“并发请求后完全挂起”问题极为关键。
✅ 注意事项:
SIGQUIT 不会终止进程(除非程序显式处理并退出),仅输出堆栈后继续运行(适合生产环境临时诊断);
若进程已完全无法响应信号(如陷入内核态长时间阻塞),可能无输出,此时需结合 pprof 或 gdb 进一步分析;
开发阶段可配合环境变量增强调试信息:
# 输出调度器每 500ms 的详细状态(含 goroutine 数量、GC、M/P 状态) GODEBUG=schedtrace=500 ./myserver # 启用全 goroutine 堆栈(默认已启用,但可确保) GOTRACEBACK=all ./myserver
此外,强烈建议在部署前启用 go run -race 或 go build -race 编译带竞态检测的二进制,它能在运行时动态捕获你代码中 i++ 那类未同步的共享变量访问,并立即 panic 报错,从根本上预防此类隐患。对于已上线服务,SIGQUIT 是你最值得掌握的“急救键”。


















