funcmstart1() {// 启动过程时 _g_ = m0.g0 _g_ :=getg()if _g_ != _g_.m.g0 {throw("bad runtime·mstart") }// Record top of stack for use by mcall.// Once we call schedule we're never coming back,// so other calls can reuse this stack space.// // 一旦调用 schedule() 函数,永不返回// 所以栈帧可以被复用gosave(&_g_.m.g0.sched) _g_.m.g0.sched.pc =^uintptr(0) // make sure it is never usedasminit()minit()// ……………………// 执行启动函数。初始化过程中,fn == nilif fn := _g_.m.mstartfn; fn !=nil {fn() }if _g_.m.helpgc !=0 { _g_.m.helpgc =0stopm() } elseif _g_.m !=&m0 {acquirep(_g_.m.nextp.ptr()) _g_.m.nextp =0 }// 进入调度循环。永不返回schedule()}
// 执行一轮调度器的工作:找到一个 runnable 的 goroutine,并且执行它// 永不返回funcschedule() {// _g_ = 每个工作线程 m 对应的 g0,初始化时是 m0 的 g0 _g_ :=getg()// ……………………top:// ……………………var gp *gvar inheritTime bool// ……………………if gp ==nil {// Check the global runnable queue once in a while to ensure fairness.// Otherwise two goroutines can completely occupy the local runqueue// by constantly respawning each other.// 为了公平,每调用 schedule 函数 61 次就要从全局可运行 goroutine 队列中获取if _g_.m.p.ptr().schedtick%61==0&& sched.runqsize >0 {lock(&sched.lock)// 从全局队列最大获取 1 个 gorutine gp =globrunqget(_g_.m.p.ptr(), 1)unlock(&sched.lock) } }// 从 P 本地获取 G 任务if gp ==nil { gp, inheritTime =runqget(_g_.m.p.ptr())if gp !=nil&& _g_.m.spinning {throw("schedule: spinning with local work") } }if gp ==nil {// 从本地运行队列和全局运行队列都没有找到需要运行的 goroutine,// 调用 findrunnable 函数从其它工作线程的运行队列中偷取,如果偷不到,则当前工作线程进入睡眠// 直到获取到 runnable goroutine 之后 findrunnable 函数才会返回。 gp, inheritTime =findrunnable() // blocks until work is available }// This thread is going to run a goroutine and is not spinning anymore,// so if it was marked as spinning we need to reset it now and potentially// start a new spinning M.if _g_.m.spinning {resetspinning() }if gp.lockedm !=nil {// Hands off own p to the locked m,// then blocks waiting for a new p.startlockedm(gp)goto top }// 执行 goroutine 任务函数// 当前运行的是 runtime 的代码,函数调用栈使用的是 g0 的栈空间// 调用 execute 切换到 gp 的代码和栈空间去运行execute(gp, inheritTime)}
调用 runqget,从 P 本地可运行队列先选出一个可运行的 goroutine;为了公平,调度器每调度 61 次的时候,都会尝试从全局队列里取出待运行的 goroutine 来运行,调用 globrunqget;如果还没找到,就要去其他 P 里面去偷一些 goroutine 来执行,调用 findrunnable 函数。