You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go pprof是否随机选线程接收信号?为何栈采样数与预期不符

Go 1.18 前 pprof CPU 采样逻辑相关问题解答

1. Go pprof 是否会随机选择一个线程接收信号?

Go 1.18 版本之前的 pprof CPU 采样实现不会主动随机选择线程接收信号,实际的信号投递和处理逻辑如下:

  • 调用runtime.SetCPUProfileRate(hz)时,runtime 仅会在进程初始启动的主线程上调用setitimer(ITIMER_PROF)配置内核计时器,这个计时器统计的是整个进程下所有线程的累计CPU使用时长,不是单线程的CPU时长。
  • 当累计CPU时长达到1/hz的阈值时,内核会向进程发送SIGPROF信号。Linux 内核不会固定把信号投递给设置计时器的主线程,而是会按照自身调度规则,将信号投递给当时正在CPU上运行、且未屏蔽该信号的任意一个属于该进程的线程,这个选择逻辑完全由内核控制,Go runtime 不会干预。
  • 旧版本实现的核心缺陷是:所有运行Go代码的工作线程都没有屏蔽SIGPROF,但只有主线程初始化了采样信号处理所需的备用信号栈(sigaltstack)和完整的处理上下文。信号投递给工作线程时大概率会因为上下文缺失被丢弃,少数情况下会触发异常处理逻辑。

2. 为什么实际采样得到的栈追踪数是240条/秒,和理论计算值不符?

你提到的「profileHz非零的线程每秒收到100个信号、对应100条栈追踪」的推论,建立在「计时器按单线程CPU时间计时、信号均匀投递且单次信号仅触发一次采样」的理想前提下,旧版本实现完全不满足这个前提,实际计数偏差来自两个核心原因:

  • 首先是信号触发总数的计算偏差:因为计时器统计的是全进程累计CPU时间,20核满负载场景下,进程每秒累计消耗20秒CPU时间,按100Hz的配置,内核每秒本该触发20*100=2000次SIGPROF信号,不是单线程场景下的100次。
  • 其次是旧版本的信号处理bug:当SIGPROF被投递给没有初始化采样上下文的工作线程时,工作线程的轻量信号处理逻辑不会正常完成采样,也不会正确消耗信号,反而会把信号转发给主线程处理,这种场景下工作线程和主线程可能各自记录一次栈追踪,相当于单次信号触发了两次采样。

两种因素叠加后,绝大多数投递给工作线程的信号因为上下文缺失直接丢失,少部分信号触发了重复采样,最终既达不到预期的2000条/秒的采样率,也不会落在单线程100条/秒的理论值上,在测试场景下就测出了平均240条/秒的结果。


内容的提问来源于stack exchange,提问作者jie yang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:54:18