生生世世 —— schedule 的轮回(七)
上一講,我們講完 main goroutine 以及普通 goroutine 的退出過(guò)程。main goroutine 退出后直接調(diào)用 exit(0) 使得整個(gè)進(jìn)程退出,而普通 goroutine 退出后,則進(jìn)行了一系列的調(diào)用,最終又切到 g0 棧,執(zhí)行 schedule 函數(shù)。
從前面的文章我們知道,普通 goroutine(gp)就是在 schedule 函數(shù)中被選中,然后才有機(jī)會(huì)執(zhí)行。而現(xiàn)在,gp 執(zhí)行完之后,再次進(jìn)入 schedule 函數(shù),形成一個(gè)循環(huán)。這個(gè)循環(huán)太長(zhǎng)了,我們有必要再重新梳理一下。
如圖所示,rt0_go 負(fù)責(zé) Go 程序啟動(dòng)的所有初始化,中間進(jìn)行了很多初始化工作,調(diào)用 mstart 之前,已經(jīng)切換到了 g0 棧,圖中不同色塊表示使用不同的棧空間。
接著調(diào)用 gogo 函數(shù),完成從 g0 棧到用戶 goroutine 棧的切換,包括 main goroutine 和普通 goroutine。
之后,執(zhí)行 main 函數(shù)或者用戶自定義的 goroutine 任務(wù)。
執(zhí)行完成后,main goroutine 直接調(diào)用 eixt(0) 退出,普通 goroutine 則調(diào)用 goexit -> goexit1 -> mcall,完成普通 goroutine 退出后的清理工作,然后切換到 g0 棧,調(diào)用 goexit0 函數(shù),將普通 goroutine 添加到緩存池中,再調(diào)用 schedule 函數(shù)進(jìn)行新一輪的調(diào)度。
schedule() -> execute() -> gogo() -> goroutine 任務(wù) -> goexit() -> goexit1() -> mcall() -> goexit0() -> schedule()可以看出,一輪調(diào)度從調(diào)用 schedule 函數(shù)開(kāi)始,經(jīng)過(guò)一系列過(guò)程再次調(diào)用 schedule 函數(shù)來(lái)進(jìn)行新一輪的調(diào)度,從一輪調(diào)度到新一輪調(diào)度的過(guò)程稱之為一個(gè)調(diào)度循環(huán)。
這里說(shuō)的調(diào)度循環(huán)是指某一個(gè)工作線程的調(diào)度循環(huán),而同一個(gè)Go 程序中存在多個(gè)工作線程,每個(gè)工作線程都在進(jìn)行著自己的調(diào)度循環(huán)。
從前面的代碼分析可以得知,上面調(diào)度循環(huán)中的每一個(gè)函數(shù)調(diào)用都沒(méi)有返回,雖然 goroutine任務(wù)->goexit()->goexit1()->mcall() 是在 g2 的棧空間執(zhí)行的,但剩下的函數(shù)都是在 g0 的棧空間執(zhí)行的。
那么問(wèn)題就來(lái)了,在一個(gè)復(fù)雜的程序中,調(diào)度可能會(huì)進(jìn)行無(wú)數(shù)次循環(huán),也就是說(shuō)會(huì)進(jìn)行無(wú)數(shù)次沒(méi)有返回的函數(shù)調(diào)用,大家都知道,每調(diào)用一次函數(shù)都會(huì)消耗一定的棧空間,而如果一直這樣無(wú)返回的調(diào)用下去無(wú)論 g0 有多少棧空間終究是會(huì)耗盡的,那么這里是不是有問(wèn)題?其實(shí)沒(méi)有問(wèn)題!關(guān)鍵點(diǎn)就在于,每次執(zhí)行 mcall 切換到 g0 棧時(shí)都是切換到 g0.sched.sp 所指的固定位置,這之所以行得通,正是因?yàn)閺?schedule 函數(shù)開(kāi)始之后的一系列函數(shù)永遠(yuǎn)都不會(huì)返回,所以重用這些函數(shù)上一輪調(diào)度時(shí)所使用過(guò)的棧內(nèi)存是沒(méi)有問(wèn)題的。
上面這三段引用自參考資料【阿波張 非 main goroutine 的退出及調(diào)度循環(huán)】。
我再解釋一下:棧空間在調(diào)用函數(shù)時(shí)會(huì)自動(dòng)“增大”,而函數(shù)返回時(shí),會(huì)自動(dòng)“減小”,這里的增大和減小是指棧頂指針 SP 的變化。上述這些函數(shù)都沒(méi)有返回,說(shuō)明調(diào)用者不需要用到被調(diào)用者的返回值,有點(diǎn)像“尾遞歸”。
因?yàn)?g0 一直沒(méi)有動(dòng)過(guò),所有它之前保存的 sp 還能繼續(xù)使用。每一次調(diào)度循環(huán)都會(huì)覆蓋上一次調(diào)度循環(huán)的棧數(shù)據(jù),完美!
參考資料
【阿波張 非 main goroutine 的退出及調(diào)度循環(huán)】https://mp.weixin.qq.com/s/XttP9q7-PO7VXhskaBzGqA總結(jié)
以上是生活随笔為你收集整理的生生世世 —— schedule 的轮回(七)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 三足鼎立 —— GPM 到底是什么?(一
- 下一篇: 千难万险 —— goroutine 从生