1. 主要組成部分
Go語言的GMP調度器基于四個核心數據結構:g
、m
、p
和schedt
。
1.1 主要常量解讀
1.1.1G 狀態常量
const (_Gidle = iota //剛分配尚未初始化的 G_Grunnable//已在運行隊列上,未執行用戶代碼;棧未被該 G 擁有_Grunning//正在執行用戶代碼;已分配 M 與 P;不在運行隊列上;棧由該 G 擁有_Gsyscall//正在執行系統調用;不執行用戶代碼;不在運行隊列上;已分配 M;棧由該 G 擁有_Gwaiting//在 runtime 內部阻塞(如通道、鎖、定時器等);不執行用戶代碼;不在運行隊列上;通常不擁有棧(特定通道路徑在鎖下可讀寫)_Gmoribund_unused//預留給調試器(gdb)_Gdead//當前未使用的 G(剛退出、在空閑鏈表、或初始化中);不執行用戶代碼;可能有或沒有棧;由處理其回收/復用的 M 暫時擁有_Genqueue_unused_Gcopystack//正在進行棧移動/拷貝;不執行用戶代碼;不在運行隊列上;棧由發起拷貝的一方擁有_Gpreempted//因 suspendG 搶占而自停,類似 _Gwaiting,但尚無人負責將其 ready;需要某個 suspendG 將狀態 CAS 為 _Gwaiting 并負責喚醒//GC 掃描期疊加位,用于標注 goroutine 棧掃描/自掃描期間的狀態,除了_Gscanrunning_Gscan = 0x1000_Gscanrunnable = _Gscan + _Grunnable_Gscanrunning = _Gscan + _Grunning_Gscansyscall = _Gscan + _Gsyscall_Gscanwaiting = _Gscan + _Gwaiting_Gscanpreempted = _Gscan + _Gpreempted
)
1.1.2 P的狀態常量
const (// P status_Pidle = iota // 空閑:未用于運行用戶代碼或調度器,通常在空閑隊列_Prunning // 運行中:被某個 M 持有,用于運行用戶代碼或調度器_Psyscall // 系統調用關聯:不運行用戶代碼,與系統調用中的 M 有親和性,可被其他 M 竊取_Pgcstop // GC 停止:STW 期間暫停,由發起 STW 的 M 持有_Pdead // 已死亡:GOMAXPROCS 變小后不再使用,資源基本被剝離
)
1.2 重要數據結構
1.2.1 gobuf的結構體
// 源碼位置:go/src/runtime/runtime2.go
type gobuf struct {// sp, pc, g的偏移量是已知的(硬編碼在)libmach中//// ctxt在GC方面是不尋常的:它可能是堆分配的funcval,// 所以GC需要跟蹤它,但它需要從匯編中設置和清除,// 在那里很難有寫屏障。然而,ctxt實際上是一個保存的活動寄存器,// 我們只在真實寄存器和gobuf之間交換它。// 因此,我們在棧掃描期間將其視為根,這意味著保存和恢復它的匯編不需要寫屏障// 它仍然被類型化為指針,以便Go的任何其他寫入都獲得寫屏障sp uintptr // 棧指針pc uintptr // 程序計數器g guintptr // goroutine指針(繞過寫屏障)ctxt unsafe.Pointer // 上下文指針,通常指向funcvallr uintptr // 鏈接寄存器(用于某些架構)bp uintptr // 基指針(用于啟用幀指針的架構)//ret uintptr // 1.24.4被刪除 返回值 現在直接通過寄存器或 goroutine 的棧傳遞返回值
}
主要作用:
gobuf是運行時“協程上下文快照”的結構;調度、棧切換、cgo?邊界、棧擴容等一切需要“暫停-繼續”的地方,都會寫/讀它;真正的保存和跳轉由?gosave/gogo?這類匯編原語完成。
1.2.2 G的結構體
type g struct {// Stack parameters.// stack 描述實際棧內存區間: [stack.lo, stack.hi)。// stackguard0 是 Go 棧增長前序中比較的棧指針。// 通常為 stack.lo+StackGuard,但可設為 StackPreempt 以觸發搶占。// stackguard1 是 //go:systemstack 棧增長前序中比較的棧指針。// 在 g0 和 gsignal 棧上為 stack.lo+StackGuard;// 在其他 goroutine 棧上為 ~0,以觸發 morestackc 并導致崩潰。stack stack // 偏移量由 runtime/cgo 已知stackguard0 uintptr // 偏移量由 liblink 已知stackguard1 uintptr // 偏移量由 liblink 已知_panic *_panic // 最內層 panic,偏移量由 liblink 已知_defer *_defer // 最內層 deferm *m // 當前綁定的 M,偏移量由 arm liblink 已知sched gobuf // 調度上下文(保存寄存器、PC、SP 等)// 系統調用相關上下文(用于 GC 時保留)syscallsp uintptr // 若 status==Gsyscall,此處存 sched.spsyscallpc uintptr // 若 status==Gsyscall,此處存 sched.pcsyscallbp uintptr // 若 status==Gsyscall,此處存 sched.bp 用于回溯stktopsp uintptr // 回溯時期望的棧頂 SP// param 是通用指針參數字段,用于在特定場景傳遞值:// 1. channel 操作喚醒阻塞 goroutine 時,指向該次阻塞的 sudog;// 2. gcAssistAlloc1 通知調用者完成 GC 周期(因棧可能已搬遷);// 3. debugCallWrap 傳參給新 goroutine(運行時禁止閉包分配);// 4. panic 恢復返回到某幀時,指向 savedOpenDeferState。param unsafe.Pointeratomicstatus atomic.Uint32 // 原子狀態stackLock uint32 // sigprof/scang 鎖;TODO: 合并到 atomicstatusgoid uint64 // goroutine 唯一 IDschedlink guintptr // 全局可運行隊列鏈接waitsince int64 // 估計阻塞開始時間waitreason waitReason // 若 status==Gwaiting,記錄原因// 搶占控制preempt bool // 搶占信號(等價于 stackguard0 = stackpreempt)preemptStop bool // 搶占時轉入 _Gpreempted,否則僅去調度preemptShrink bool // 在同步安全點收縮棧// asyncSafePoint 表示 g 在異步安全點停下,此時棧上可能無精確指針信息asyncSafePoint bool// paniconfault 在意外故障地址時 panic 而非直接 crashpaniconfault bool// gcscandone 表示棧掃描已完成,受 status 的 _Gscan 位保護gcscandone bool// throwsplit 禁止在此 g 上進行棧分裂throwsplit bool// activeStackChans 表示未加鎖 channel 引用此棧,收縮時需加鎖保護activeStackChans bool// parkingOnChan 表示即將 park 在 chansend/chanrecv 上,標記棧收縮不安全點parkingOnChan atomic.Bool// inMarkAssist 表示是否在執行 mark assist(執行跟蹤使用)inMarkAssist boolcoroexit bool // coroswitch_m 的參數// raceignore 忽略競態檢測事件raceignore int8 // 忽略競態檢測事件nocgocallback bool // 禁止從 C 回調到 Gotracking bool // 是否跟蹤此 G 的調度延遲統計trackingSeq uint8 // 決定是否跟蹤的序列號trackingStamp int64 // 開始跟蹤的時間戳runnableTime int64 // 可運行時間累計(運行時清零),僅在跟蹤時使用lockedm muintptr // LockThread 時鎖定的 MfipsIndicator uint8 // FIPS 模式指示syncSafePoint bool // 是否停在同步安全點runningCleanups atomic.Bool // 是否正在運行清理函數sig uint32 // 信號編號writebuf byte // 信號處理寫緩沖區sigcode0 uintptr // 信號處理相關寄存器sigcode1 uintptrsigpc uintptrparentGoid uint64 // 創建此 goroutine 的父 goidgopc uintptr // 創建此 goroutine 的 go 語句的 PCancestors *ancestorInfo // debug.tracebackancestors 模式下的祖先鏈startpc uintptr // goroutine 函數入口 PCracectx uintptr // 競態檢測上下文// waiting 指向當前 g 等待的 sudog 鏈表(elem 有效)waiting *sudogcgoCtxtuintptr // cgo 回溯上下文labels unsafe.Pointer // 性能分析器標簽timer *timer // time.Sleep 緩存的定時器sleepWhen int64 // 睡眠到期時間selectDone atomic.Uint32 // 是否參與 select 及是否贏得喚醒競賽// goroutineProfiled 表示當前 goroutine 棧在 profile 中的狀態goroutineProfiled goroutineProfileStateHoldercoroarg *coro // 協程切換參數bubble *synctestBubble // 同步測試氣泡// Per-G 追蹤狀態trace gTraceState// GC 相關狀態// gcAssistBytes 為 GC 輔助信用額度(字節數):// >0 表示有可用額度;// <0 表示需完成掃描工作。// 通過 assist ratio 轉換為掃描工作債務。gcAssistBytes int64// valgrindStackID 在 valgrind build tag 下用于跟蹤棧內存,否則未使用valgrindStackID uintptr
}
G的狀態轉換圖
1.2.3 M的結構體
type m struct {g0 *g// g0: 持有調度堆棧的goroutinemorebuf gobuf// morebuf: 傳遞給morestack的gobuf參數divmod uint32// divmod: ARM平臺的除法/模運算分母(已知liblink,詳見cmd/internal/obj/arm/obj5.go)// 調試器未知的字段procid uint64 // 供調試器使用,但偏移量未硬編碼gsignal *g // 信號處理專用的goroutinegoSigStack gsignalStack // Go分配的信號處理堆棧sigmask sigset // 保存的信號掩碼存儲tls [tlsSlots]uintptr // 線程局部存儲(用于x86外部寄存器)mstartfn func() // M啟動函數curg *g // 當前運行的goroutinecaughtsig guintptr // 在致命信號期間運行的goroutinep puintptr // 附加的P(用于執行Go代碼,未執行Go代碼時為nil)nextp puintptr // 下一個Poldp puintptr // 執行系統調用前附加的Pid int64 // M的唯一IDmallocing int32 // 是否在分配內存throwing throwType // 當前拋出類型preemptoff string // 若非空,強制保持curg在此M上運行locks int32 // 保持鎖定的次數dying int32 // 死亡狀態標志profilehz int32 // 性能分析頻率spinning bool // M處于空閑狀態并主動尋找工作blocked bool // M被note阻塞newSigstack bool // minit在C線程中調用了sigaltstackprintlock int8 // 打印鎖incgo bool // 是否在執行cgo調用isextra bool // 是否為備用MisExtraInC bool // 是否為在C代碼中運行的備用MisExtraInSig bool // 是否為在信號處理中運行的備用MfreeWait atomic.Uint32 // 是否可以安全釋放g0并刪除M(freeMRef/freeMStack/freeMWait之一)needextram bool // 是否需要備用Mg0StackAccurate bool // g0堆棧是否具有準確邊界traceback uint8 // 回溯類型allpSnapshot []*p // 附加P時的allp快照(findRunnable釋放P后使用,否則為nil)ncgocall uint64 // 總cgo調用次數ncgo int32 // 當前進行的cgo調用次數cgoCallersUse atomic.Uint32 // 若非零,cgoCallers臨時可用cgoCallers *cgoCallers // cgo調用崩潰時的回溯信息park note // 用于阻塞M的notealllink *m // 在allm鏈表中的鏈接schedlink muintptr // 調度鏈表鏈接lockedg guintptr // 鎖定的goroutinecreatestack [32]uintptr // 創建此線程的堆棧(用于StackRecord.Stack0,必須對齊)lockedExt uint32 // 外部LockOSThread狀態追蹤lockedInt uint32 // 內部lockOSThread狀態追蹤mWaitList mWaitList // 運行時鎖等待者列表mLockProfile mLockProfile // 與runtime.lock爭用相關的字段profStackuintptr // 用于內存/阻塞/互斥鎖堆棧追蹤// wait*字段用于從gopark傳遞參數到park_m(因為低級NOSPLIT函數沒有堆棧)waitunlockf func(*g, unsafe.Pointer) bool // 解鎖函數waitlock unsafe.Pointer // 等待的鎖waitTraceSkip int // 跟蹤跳過次數waitTraceBlockReason traceBlockReason // 跟蹤阻塞原因syscalltick uint32 // 系統調用計數freelink *m // 在sched.freem鏈表中的鏈接trace mTraceState // 跟蹤狀態// 這些字段太大,不能放在低級NOSPLIT函數的堆棧中libcall libcall // 系統調用參數libcallpc uintptr // 用于CPU性能分析libcallsp uintptr // 調用堆棧指針libcallg guintptr // 當前goroutinewinsyscall winlibcall // Windows平臺的系統調用參數vdsoSP uintptr // VDSO調用中的堆棧指針(未調用時為0)vdsoPC uintptr // VDSO調用中的程序計數器// preemptGen: 記錄已完成的搶占信號次數(用于檢測搶占失敗)preemptGen atomic.Uint32// 該M是否有待處理的搶占信號signalPending atomic.Uint32// pcvalue查找緩存pcvalueCache pcvalueCachedlogPerM // 每M的日志記錄mOS // 操作系統相關字段chacha8 chacha8rand.State // ChaCha8隨機數生成器狀態cheaprand uint64 // 快速隨機數生成器// 該M持有的最多10把鎖(由鎖排序代碼維護)locksHeldLen int // 持有鎖數量locksHeld [10]heldLockInfo // 持有鎖信息數組
}
M的內存大小
const mRedZoneSize = (16 << 3) * asanenabledBit // redZoneSize(2048)type mPadded struct {m// Size the runtime.m structure so it fits in the 2048-byte size class, and// not in the next-smallest (1792-byte) size class. That leaves the 11 low// bits of muintptr values available for flags, as required by// lock_spinbit.go._ [(1 - goarch.IsWasm) * (2048 - mallocHeaderSize - mRedZoneSize - unsafe.Sizeof(m{}))]byte
}
mRedZoneSize是在啟用Ascan時,作為棧溢出檢測的區域,大小為128字節。也用于棧擴展部分,當stackstackguard0進入red zone,會觸發gorwStack擴展棧。
mPadded
?的設計目的是?通過填充字段?確保?m
?結構體大小為 2048 字節,從而在?muintptr
?中?保留低 11 位用于標志位。該設計在非 Wasm 平臺生效,Wasm 平臺因內存模型差異跳過填充。核心優化點在于?標志位復用?和?內存對齊,避免額外內存分配,提升并發性能。
1.2.3 P的結構體
type p struct {id int32 // 進程IDstatus uint32 // 狀態(如 pidle/prunning/...)link puintptr // 鏈表指針schedtick uint32 // 每次調度調用時遞增的計數器syscalltick uint32 // 每次系統調用時遞增的計數器sysmontick sysmontick // sysmon 上次觀察到的計數器m muintptr // 關聯的 M(空閑時為 nil)mcache *mcache // 本地 M 的緩存pcache pageCache // 頁緩存raceprocctx uintptr // 競態檢測上下文deferpool []*_defer // 可用 defer 結構體池(見 panic.go)deferpoolbuf [32]*_defer // defer 結構體緩沖區// 緩存的 goroutine ID,減少對 runtime·sched.goidgen 的訪問goidcache uint64 // goroutine ID 緩存起始值goidcacheend uint64 // 緩存的 goroutine ID 終止值// 可運行的 goroutine 隊列(無鎖訪問)runqhead uint32 // 隊列頭部索引runqtail uint32 // 隊列尾部索引runq [256]guintptr // 隊列數組// runnext 存儲當前 G 準備運行的下一個 G(若時間片未用盡)。// 它繼承當前時間片的剩余時間。若一組 goroutine 處于通信和等待模式中,// 此字段可將該組作為一個單元調度,避免將 goroutine 添加到隊列尾部導致的調度延遲。// 注意:其他 P 可以原子地將此字段置為零,但只有當前 P 可以原子地設置為有效 G。runnext guintptr// 可用的 G(狀態 == Gdead)gFree gList // 可回收的 G 列表sudogcache []*sudog // sudog 緩存sudogbuf [128]*sudog // sudog 緩沖區// 從堆中緩存的 mspan 對象mspancache struct {// 顯式長度字段,避免在分配路徑中使用寫屏障時的復雜性len int // 當前緩存長度buf [128]*mspan // 緩存數組}// 緩存的單個 pinner 對象,減少重復創建 pinner 的分配開銷pinnerCache *pinnertrace pTraceState // 跟蹤狀態palloc persistentAlloc // 每個 P 的持久化分配器// 每個 P 的 GC 狀態gcAssistTime int64 // GC 輔助分配所花費的時間(納秒)gcFractionalMarkTime int64 // 分數標記工作者所花費的時間(納秒,原子更新)// limiterEvent 跟蹤 GC CPU 限制器的事件limiterEvent limiterEvent// gcMarkWorkerMode 指示下一個標記工作者應運行的模式// 用于與通過 gcController.findRunnableGCWorker 選擇的工作者 goroutine 通信// 調度其他 goroutine 時,此字段必須設置為 gcMarkWorkerNotWorkergcMarkWorkerMode gcMarkWorkerMode// gcMarkWorkerStartTime 是最近標記工作者的啟動時間(納秒)gcMarkWorkerStartTime int64// gcw 是此 P 的 GC 工作緩沖區緩存// 緩沖區由寫屏障填充,由突變器輔助消耗,在特定 GC 狀態轉換時釋放gcw gcWork// wbBuf 是此 P 的 GC 寫屏障緩沖區// TODO: 考慮將其緩存在運行的 G 中wbBuf wbBufrunSafePointFn uint32 // 若為 1,調度器在下一個安全點運行 sched.safePointFn// statsSeq 是指示此 P 是否正在寫入統計信息的計數器// 偶數表示未寫入,奇數表示正在寫入statsSeq atomic.Uint32// 定時器堆timers timers// 清理塊cleanups *cleanupBlockcleanupsQueued uint64 // 此 P 隊列的清理塊數量(單調遞增)// maxStackScanDelta 累積活動 goroutine 的棧空間占用(即可能需要掃描的棧大小)// 當達到 maxStackScanSlack 或 -maxStackScanSlack 時,刷新到 gcController.maxStackScanmaxStackScanDelta int64// GC 時間統計// 與 maxStackScan 不同,該字段累積 GC 時實際觀察到的棧使用量(hi - sp)// 而非瞬時的總棧大小(hi - lo)scannedStackSize uint64 // 此 P 掃描的 goroutine 棧大小scannedStacks uint64 // 此 P 掃描的 goroutine 數量// preempt 標記此 P 需盡快進入調度器(無論當前 G 在運行什么)preempt bool// gcStopTime 是此 P 最近進入 _Pgcstop 的時間戳(納秒)gcStopTime int64
}
1.2.4 schedt結構體
type schedt struct {// goidgen 是全局唯一的 goroutine ID 生成器// lastpoll 記錄上次網絡輪詢的時間(若當前正在輪詢則為 0)// pollUntil 記錄當前輪詢的睡眠截止時間// pollingNet 表示是否有 P 正在執行非阻塞的網絡輪詢goidgen atomic.Uint64lastpoll atomic.Int64 // 上次網絡輪詢時間pollUntil atomic.Int64 // 當前輪詢的睡眠截止時間pollingNet atomic.Int32 // 1 表示有 P 正在執行非阻塞輪詢// lock 是全局調度器的互斥鎖lock mutex// 增加 nmidle、nmidlelocked、nmsys 或 nmfreed 時,必須調用 checkdead()// midle 是等待工作的空閑 M 鏈表// nmidle 是空閑 M 的數量// nmidlelocked 是被鎖定的空閑 M 數量// mnext 是已創建的 M 總數和下一個 M 的 ID// maxmcount 是允許的最大 M 數量(超過則終止)// nmsys 是系統 M 的數量(不計入死鎖檢測)// nmfreed 是累計釋放的 M 數量midle muintptr // 空閑 M 鏈表nmidle int32 // 空閑 M 數量nmidlelocked int32 // 被鎖定的空閑 M 數量mnext int64 // 已創建的 M 數量和下一個 M IDmaxmcount int32 // 最大允許的 M 數量nmsys int32 // 系統 M 數量nmfreed int64 // 累計釋放的 M 數量// ngsys 是系統 goroutine 的數量ngsys atomic.Int32// pidle 是空閑 P 鏈表// npidle 是空閑 P 數量(原子更新)// nmspinning 是自旋的 M 數量(參考 proc.go 中的“Worker thread parking/unparking”注釋)// needspinning 是是否需要自旋的標志(參考 proc.go 中的“Delicate dance”注釋,布爾值,修改時需持有 sched.lock)pidle puintptr // 空閑 P 鏈表npidle atomic.Int32nmspinning atomic.Int32needspinning atomic.Uint32// 全局可運行隊列runq gQueue// disable 控制調度器的禁用(通過 schedEnableUser 控制)// disable.user 表示是否禁用用戶 goroutine 的調度// disable.runnable 是待運行的 G 隊列disable struct {user boolrunnable gQueue // 待運行的 G 隊列}// gFree 是全局死亡 G 的緩存gFree struct {lock mutexstack gList // 帶棧的 G 列表noStack gList // 無棧的 G 列表}// sudoglock 保護 sudogcache 的互斥鎖// sudogcache 是全局 sudog 緩存sudoglock mutexsudogcache *sudog// deferlock 保護 deferpool 的互斥鎖// deferpool 是全局 defer 結構體緩存deferlock mutexdeferpool *_defer// freem 是 m.exited 被設置后等待釋放的 M 鏈表(通過 m.freelink 連接)freem *m// gcwaiting 表示 GC 是否在等待運行// stopwait 和 stopnote 用于 stop-the-world 等待// sysmonwait 表示 sysmon 是否在等待// sysmonnote 是 sysmon 的等待信號gcwaiting atomic.Bool // GC 等待運行標志stopwait int32stopnote notesysmonwait atomic.Boolsysmonnote note// safePointFn 是在下一個 GC 安全點需要調用的函數(若 p.runSafePointFn 被設置)// safePointWait 是等待計數// safePointNote 是安全點的等待信號safePointFn func(*p)safePointWait int32safePointNote note// profilehz 是 CPU 采樣率profilehz int32// procresizetime 是上次調整 gomaxprocs 的時間(納秒)// totaltime 是從 procresizetime 開始的累計運行時間procresizetime int64 // 上次調整 gomaxprocs 的時間totaltime int64 // 累計運行時間// customGOMAXPROCS 表示 GOMAXPROCS 是否被手動設置(環境變量或 runtime.GOMAXPROCS)customGOMAXPROCS bool// sysmonlock 是保護 sysmon 與運行時交互的互斥鎖// 持有該鎖可阻斷 sysmon 對運行時的操作sysmonlock mutex// timeToRun 是調度延遲分布(定義為 G 在 _Grunnable 狀態到 _Grunning 狀態的總時間)timeToRun timeHistogram// idleTime 是所有 P 的空閑時間總和(每次 GC 周期重置)idleTime atomic.Int64// totalMutexWaitTime 是 goroutine 在 _Gwaiting 狀態等待 runtime 內部鎖的總時間totalMutexWaitTime atomic.Int64// stwStoppingTimeGC/Other 是 stop-the-world 停止延遲分布(定義為 stopTheWorldWithSema 到所有 P 停止的時間)// stwStoppingTimeGC 覆蓋所有 GC 相關的 STW,stwStoppingTimeOther 覆蓋其他 STWstwStoppingTimeGC timeHistogramstwStoppingTimeOther timeHistogram// stwTotalTimeGC/Other 是 stop-the-world 總延遲分布(定義為 stopTheWorldWithSema 到 startTheWorldWithSema 的總時間)// stwTotalTimeGC 覆蓋所有 GC 相關的 STW,stwTotalTimeOther 覆蓋其他 STWstwTotalTimeGC timeHistogramstwTotalTimeOther timeHistogram// totalRuntimeLockWaitTime(加上每個 M 的 lockWaitTime)是 goroutine 在 _Grunnable 狀態且持有 M 但等待 runtime 內部鎖的總時間// 該字段存儲已退出 M 的累計時間totalRuntimeLockWaitTime atomic.Int64
}
剩余結構體作用
type libcall struct {fn uintptrn uintptr // 參數個數args uintptr // 參數列表r1 uintptr // 返回值1r2 uintptr // 返回值2err uintptr // 錯誤號
}// Stack 描述了 Go 運行時的執行棧。
// 棧的邊界恰好是 [lo, hi),
// 兩端沒有任何隱式的數據結構。
type stack struct {lo uintptrhi uintptr
}// heldLockInfo 提供已持有的鎖及該鎖等級的信息
type heldLockInfo struct {lockAddr uintptrrank lockRank
}
其中libcall在匯編/系統層面,runtime 會構造一個 libcall,把要調用的函數地址、參數列表打包到它的各字段里,然后由通用的調用入口(如 asm/syscall 實現)讀取這些字段并真正發起調用,返回值和 errno 也寫回到這里。
每個 G 對象里有一個 stack 字段,用 lo、hi 精確標識它的棧內存區域(lo ≤ sp < hi)。GC、棧擴展/收縮、調度等子系統都依賴這兩個邊界來判斷棧是否需要 grow/shrink,以及掃描活躍幀時的地址合法性
Go 運行時為了在調試模式下檢測可能的死鎖或鎖順序反轉,會給每把鎖分配一個 rank。每當 G 獲取一把鎖,就往它的 heldLocks 列表里插入一個 heldLockInfo。釋放時再刪掉。這樣就能在運行時斷言“只允許按 rank 升序獲取鎖”,及時報告不安全的鎖順序。
參考文獻
https://juejin.cn/post/7519334402688368667#heading-11
https://zhuanlan.zhihu.com/p/67852800