funcpreemptone(_p_ *p)bool { mp := _p_.m.ptr() if mp == nil || mp == getg().m { returnfalse } gp := mp.curg if gp == nil || gp == mp.g0 { returnfalse } gp.preempt = true // Every call in a go routine checks for stack overflow by // comparing the current stack pointer to gp->stackguard0. // Setting gp->stackguard0 to StackPreempt folds // preemption into the normal stack overflow check. gp.stackguard0 = stackPreempt returntrue }
funcpreemptone(_p_ *p)bool { mp := _p_.m.ptr() if mp == nil || mp == getg().m { returnfalse } gp := mp.curg if gp == nil || gp == mp.g0 { returnfalse } gp.preempt = true // Every call in a go routine checks for stack overflow by // comparing the current stack pointer to gp->stackguard0. // Setting gp->stackguard0 to StackPreempt folds // preemption into the normal stack overflow check. gp.stackguard0 = stackPreempt // Request an async preemption of this P. if preemptMSupported && debug.asyncpreemptoff == 0 { _p_.preempt = true preemptM(mp) }
/** 创建新内核线程执行链: newm() ---------> newm1() ----> 是cgo mstart() -> mstart1() -> mstart0 -> initsig() templateThread() -----↗ ↘非cgo newosproc() --↗ **/ funcinitsig(preinit bool) { if !preinit { // It's now OK for signal handlers to run. signalsOK = true } // For c-archive/c-shared this is called by libpreinit with // preinit == true. if (isarchive || islibrary) && !preinit { return } for i := uint32(0); i < _NSIG; i++ { t := &sigtable[i] if t.flags == 0 || t.flags&_SigDefault != 0 { continue } // We don't need to use atomic operations here because // there shouldn't be any other goroutines running yet. fwdSig[i] = getsig(i) if !sigInstallGoHandler(i) { // Even if we are not installing a signal handler, // set SA_ONSTACK if necessary. if fwdSig[i] != _SIG_DFL && fwdSig[i] != _SIG_IGN { setsigstack(i) } elseif fwdSig[i] == _SIG_IGN { sigInitIgnored(i) } continue } handlingSig[i] = 1 setsig(i, funcPC(sighandler)) } }
funcsighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { // ... ... if sig == sigPreempt { // Might be a preemption signal. doSigPreempt(gp, c) // Even if this was definitely a preemption signal, it // may have been coalesced with another signal, so we // still let it through to the application. } // ... ... }
核心逻辑在这一段,if sig == sigPreempt,执行 doSigPreempt,sigPreempt上边介绍过,它的值为 _SIGURG,因此这里便是处理抢占逻辑的核心方法,继续挖掘 doSigPreempt:
// doSigPreempt handles a preemption signal on gp. funcdoSigPreempt(gp *g, ctxt *sigctxt) { if wantAsyncPreempt(gp) && isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()) { // Inject a call to asyncPreempt. ctxt.pushCall(funcPC(asyncPreempt)) } // Acknowledge the preemption. atomic.Xadd(&gp.m.preemptGen, 1) atomic.Store(&gp.m.signalPending, 0) } // asyncPreempt saves all user registers and calls asyncPreempt2. // // When stack scanning encounters an asyncPreempt frame, it scans that // frame and its parent frame conservatively. // // asyncPreempt is implemented in assembly. funcasyncPreempt() //go:nosplit funcasyncPreempt2() { gp := getg() gp.asyncSafePoint = true if gp.preemptStop { mcall(preemptPark) } else { mcall(gopreempt_m) } gp.asyncSafePoint = false }