func f1() {
fmt.Println("This is f1")
}
func f2() {
// 死循环
for {
}
fmt.Println("This is f2")
}
func main() {
runtime.GOMAXPROCS(1)
go f1()
go f2()
time.Sleep(100 * time.Millisecond)
fmt.Println("success")
}
// This is f1
// success
func sysmon() {
//无限循环
for {
//渐进式休眠,最短20us,最长10ms
if idle == 0 {
delay = 20
} else if idle > 50 { //前50轮休眠时间都是20us,50轮后double
delay *= 2
}
if delay > 10*1000 { // up to 10ms
delay = 10 * 1000
}
usleep(delay) //休眠
// retake 对所有的 G 进行重整
if retake(now) != 0 {
idle = 0 //retake成功则重置轮数
} else {
idle++ //否则轮数+1
}
}
}
//retake 对所有的 G 进行重整,发起抢占
func retake(now int64) uint32 {
//加锁
lock(&allpLock)
for i := 0; i < len(allp); i++ {
_p_ := allp[i]
pd := &_p_.sysmontick
s := _p_.status
if s == _Psyscall {
// P 处于系统调用之中,抢占检查条件比较复杂,略
// 与下面的区别是,syscall 本就是通过 g0 无 P 执行的,因此这个 P 此时挂在这个 M 上是浪费的,需要把这个 P 给别的 M
} else if s == _Prunning { // P处于运行状态,检查其是否运行得太久了
// forcePreemptNS = 10ms,如果协程独占P超过10ms,就需要进行抢占了
if pd.schedwhen+forcePreemptNS <= now {
preemptone(_p_) // 修改 G 的标记为可被抢占
sysretake = true
}
}
}
}
//程序初始化时,监听SIGURG信号
//src/runtime/proc.go
func mstartm0() {
initsig(false)
}
//src/runtime/signal_unix.go
func initsig(preinit bool) {
// 对于一个需要设置 sighandler 的信号,会通过 setsig 来设置信号对应的动作(action):
setsig(i, funcPC(sighandler))
}
//runtime/preempt.go
//这个函数里包含了对各种信号量的处理,其中就包括SIGURG
func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
if sig == sigPreempt && debug.asyncpreemptoff == 0 {
doSigPreempt(gp, c)
}
}
func doSigPreempt(gp *g, ctxt *sigctxt) {
// 判断异步安全点
if wantAsyncPreempt(gp) {
if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()); ok {
// Adjust the PC and inject a call to asyncPreempt.
ctxt.pushCall(funcPC(asyncPreempt), newpc)
}
}
// 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.
// 这个函数是用汇编实现的,实际调用了asyncPreempt2
func asyncPreempt()
//go:nosplit
func asyncPreempt2() {
gp := getg()
gp.asyncSafePoint = true
if gp.preemptStop { // GC触发的抢占。GC时,会暂停所有G的抢占,所以叫 preemptStop。只有 GC 自己能抢占
mcall(preemptPark) // 里面调用的 runtime.schedule()
} else {
mcall(gopreempt_m) // 里面调用的 goschedImpl,goschedImpl 调用的runtime.schedule()
}
gp.asyncSafePoint = false
}
// runtime/proc.go
// preemptPark parks gp and puts it in _Gpreempted.
func preemptPark(gp *g) {
casGToPreemptScan(gp, _Grunning, _Gscan|_Gpreempted) // 修改 G 的状态为抢占中
dropg() // 解绑 M 和 G
casfrom_Gscanstatus(gp, _Gscan|_Gpreempted, _Gpreempted) // 修改 G 的状态为被抢占
schedule() // 调度
}
// 解绑 M 和 G
func dropg() {
_g_ := getg()
setMNoWB(&_g_.m.curg.m, nil) // 将 G 的 M 设置为 nil
setGNoWB(&_g_.m.curg, nil) // 将 M 的 curg 设置为 nil
}