Go 语言的信号式抢占原理

从 Go 1.14 开始,通过使用信号,Go 语言实现了调度和 GC 过程中的真“抢占“。

抢占流程由抢占的发起方向被抢占线程发送 SIGURG 信号。

当被抢占线程收到信号后,进入 SIGURG 的处理流程,将 asyncPreempt 的调用强制插入到用户当前执行的代码位置。

本节会对该过程进行详尽分析。

抢占发起的时机

抢占会在下列时机发生:

  • STW 期间
  • 在 P 上执行 safe point 函数期间
  • sysmon 后台监控期间
  • gc pacer 分配新的 dedicated worker 期间
  • panic 崩溃期间

图片

除了栈扫描,所有触发抢占最终都会去执行 preemptone 函数。栈扫描流程比较特殊:

www.png

从这些流程里,我们挑出三个来一探究竟。

STW 抢占

图片

上图是现在 Go 语言的 GC 流程图,在两个 STW 阶段都需要将正在执行的线程上的 running 状态的 goroutine 停下来。

func stopTheWorldWithSema() {
  .....
  preemptall()
  .....
  // 等待剩余的 P 主动停下
  if wait {
    for {
      // wait for 100us, then try to re-preempt in case of any races
      // 等待 100us,然后重新尝试抢占
      if notetsleep(&sched.stopnote, 100*1000) {
        noteclear(&sched.stopnote)
        break
      }
      preemptall()
    }
  }

GC 栈扫描

goroutine 的栈是 GC 扫描期间的根,所有 markroot 中需要将用户的 goroutine 停下来,主要是 running 状态:

func markroot(gcw *gcWork, i uint32) {
  // Note: if you add a case here, please also update heapdump.go:dumproots.
  switch {
  ......
  default:
    // the rest is scanning goroutine stacks
    var gp *g
    ......

    // scanstack must be done on the system stack in case
    // we're trying to scan our own stack.
    systemstack(func() {
      stopped := suspendG(gp)
      scanstack(gp, gcw)
      resumeG(stopped)
    })
  }
}

suspendG 中会调用 preemptM -> signalM 对正在执行的 goroutine 所在的线程发送抢占信号。

sysmon 后台监控

func sysmon() {
  idle := 0 // how many cycles in succession we had not wokeup somebody
  for {
    ......
    // retake P's blocked in syscalls
    // and preempt long running G's
    if retake(now) != 0 {
      idle = 0
    } else {
      idle++
    }
  }
}

执行 syscall 太久的,需要将 P 从 M 上剥离;运行用户代码太久的,需要抢占停止该 goroutine 执行。这里我们只看抢占 goroutine 的部分:

const forcePreemptNS = 10 * 1000 * 1000 // 10ms

func retake(now int64) uint32 {
  ......
  for i := 0; i < len(allp); i++ {
    _p_ := allp[i]
    s := _p_.status
    if s == _Prunning || s == _Psy
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值