Go1.25的源码分析-src/runtime/runtime1.go(GMP)g

1. 主要组成部分

Go语言的GMP调度器基于四个核心数据结构:gmpschedt

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96354.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用jwt+redis实现单点登录

首先理一下登录流程 前端登录—>账号密码验证—>成功返回token—>后续请求携带token---->用户异地登录---->本地用户token不能用&#xff0c;不能再访问需要携带token的网页 jwt工具类 package com.nageoffer.shortlink.admin.util;import cn.hutool.core.util.…

Trae配置rules与MCP

这个文章不错&#xff0c;不过如果只是看&#xff0c;还感受不到作者的震撼&#xff0c;所以我自己实操了一下&#xff0c;深受震动&#xff0c;也希望看到这篇文章的人也自己实操一下。 与Cursor结对编程的四个月&#xff0c;我大彻大悟了&#xff01; 学到了什么 无论是熟悉…

对抗攻击与防御:如何保护视觉模型安全?

对抗攻击与防御:如何保护视觉模型安全? 前言 一、对抗攻击的基本原理 二、对抗攻击的主要类型 2.1 白盒攻击 2.2 黑盒攻击 三、对抗攻击的常见形式 3.1 定向攻击 3.2 非定向攻击 四、对抗防御的核心思路 五、常见的对抗防御方法 5.1 对抗训练 5.2 输入预处理 5.3 防御蒸馏 六…

区块链开发:Solidity 智能合约安全审计要点

本文聚焦区块链开发中 Solidity 智能合约的安全审计要点。首先概述智能合约安全审计的重要性&#xff0c;接着详细介绍常见的安全漏洞&#xff0c;如重入攻击、整数溢出与下溢等&#xff0c;以及对应的审计方法。还阐述了审计的具体流程&#xff0c;包括自动化工具检测、手动代…

C++ 新手第一个练手小游戏:井字棋

1. 引言 介于身边有特别多没有学习过编程&#xff0c;或者有一定C语言、python或是Java基础的但是没有接触过C的新手朋友&#xff0c;我想可以通过一个很简单的小项目作为挑战&#xff0c;帮助大家入门C。 今天&#xff0c;我们将挑战一个对新手来说稍微复杂一点&#xff0c;…

透射TEM 新手入门:快速掌握核心技能

目录 简介​ 一、TEM 基本知识 1. 核心原理&#xff08;理解图像本质&#xff09;​ 2. 关键结构与成像模式&#xff08;对应图像类型&#xff09;​ 二、TEM 数据处理 1. 预处理&#xff08;通用步骤&#xff09;​ 2. 衍射花样&#xff08;SAED&#xff09;处理&#x…

day075-MySQL数据库服务安装部署与基础服务管理命令

文章目录0. 老男孩思想-老男孩名言警句1. 数据库服务安装部署1.1 下载安装包1.2 系统环境准备1.2.1 关闭防火墙1.2.2 关闭selinux1.2.3 安装依赖软件1.2.4 卸载冲突软件1.3 安装程序1.3.1 上传软件包1.3.2 配置环境变量1.3.3 创建数据库存储数据目录1.3.4 创建数据库程序管理用…

Qt二维码生成器项目开发教程 - 从零开始构建专业级QR码生成工具

Qt二维码生成器项目开发教程 - 从零开始构建专业级QR码生成工具 项目概述 本项目是一个基于Qt框架开发的专业级二维码生成器&#xff0c;集成了开源的qrencode库&#xff0c;提供完整的QR码生成、预览、保存和分享功能。项目采用C语言开发&#xff0c;使用Qt的信号槽机制实现…

LLaVA-3D,Video-3D LLM,VG-LLM,SPAR论文解读

目录 一、LLaVA-3D 1、概述 2、方法 3、训练过程 4、实验 二、Video-3D LLM 1、概述 2、方法 3、训练过程 4、实验 三、SPAR 1、概述 2、方法 4、实验 四、VG-LLM 1、概述 2、方法 3、方法 4、实验 一、LLaVA-3D 1、概述 空间关系不足&#xff1a;传…

Spring两个核心IoCDI(二)

DI&#xff08;依赖注入&#xff09;就是从IoC容器中获取对象并赋值给某个属性&#xff0c;这就是依赖注入的过程。 关于依赖注入有3种方式&#xff1a; 1、属性注入 2、构造方法注入 3、setter注入 目录 1、属性注入 2、 构造方法注入 3、Setter方法注入 4、3种注入方式优…

广东省省考备考(第八十三天8.21)——言语、判断推理(强化训练)

言语理解与表达 错题解析 文段开篇介绍足够的执法权限对于基层治理高效运行的重要性&#xff0c;接着从两方面进行论证&#xff0c;介绍权限不足和权限过度下放对基层治理的负面影响&#xff0c;最后通过“因此”进行总结&#xff0c;强调一方面要完善执法目录动态调整机制和制…

字符串与算法题详解:最长回文子串、IP 地址转换、字符串排序、蛇形矩阵与字符串加密

字符串与算法题详解&#xff1a;最长回文子串、IP 地址转换、字符串排序、蛇形矩阵与字符串加密 前言 在编程题训练中&#xff0c;字符串相关的题目非常常见。本文将结合几个典型的例题&#xff0c;详细解析它们的解题思路和实现方式&#xff0c;帮助初学者循序渐进地掌握常用技…

从协同设计到绿色制造:工业云渲染的价值闭环

在智能制造、建筑工程、能源电力、船舶海工等工业场景中&#xff0c;3D可视化已从传统的桌面端逐步向Web端迁移&#xff0c;Web 3D凭借其跨平台、轻量化、实时交互等特性&#xff0c;已成为企业构建数字孪生、实现远程协作、推动云端交付的重要工具。这场技术变革不仅改变了工业…

算法第五十一天:图论part02(第十一章)

1.岛屿数量 99. 岛屿数量 &#x1f31f; 思路总结 — DFS 版 1️⃣ 问题本质 给定一个二维矩阵 grid&#xff0c;1 表示陆地&#xff0c;0 表示水 统计岛屿数量&#xff0c;每个岛屿由上下左右相邻的陆地组成 本质是 在二维网格中找连通块 的问题。 2️⃣ 核心思路 遍历矩阵…

杰里708n tws api 简介

/** 通过搜索码搜索tws设备*/int tws_api_search_sibling_by_code();/**打开可发现, 可连接&#xff0c;可被手机和tws搜索到*/int tws_api_wait_pair_by_code(u16 code, const char *name, int timeout_ms);int tws_api_wait_pair_by_ble(u16 code, const char *name, int tim…

高调光比 LED 恒流驱动芯片方案详解AP5165B:36V/1A

AP5165B 是深圳市世微半导体有限公司推出的一款高性能、连续电流模式的降压型&#xff08;Buck&#xff09;LED 恒流驱动芯片。该芯片适用于输入电压高于 LED 电压的应用场景&#xff0c;可驱动单颗或多颗串联的 LED&#xff0c;输出电流最高可达 1A&#xff0c;广泛用于非隔离…

【从零构建企业级线程池管理系统:Python并发编程实战指南】

从零构建企业级线程池管理系统&#xff1a;Python并发编程实战指南 技术博客 | 深入探索Python并发编程、Web开发与现代软件架构设计的完整实践 &#x1f680; 项目背景 在当今高并发的互联网时代&#xff0c;线程池作为并发编程的核心组件&#xff0c;其管理和监控能力直接影…

飞牛系统总是死机,安装个工具查看一下日志

崩溃转储 (kernel crash dump)如果你怀疑是内核 panic&#xff0c;可以开启 kdump 或 kernel crash dump。 安装&#xff1a;sudo apt install kdump-tools # Debian/Ubuntu sudo systemctl enable kdump 下次死机时&#xff0c;系统会把内存 dump 到 /var/crash 里。sudo syst…

2025年AI Agent技术深度解析:原理、应用与未来趋势

一、引言随着人工智能技术的飞速发展&#xff0c;AI Agent&#xff08;智能体&#xff09;作为人工智能领域的重要分支&#xff0c;正逐渐成为推动各行业智能化转型的关键力量。AI Agent具备自主感知、决策和执行能力&#xff0c;能够在复杂环境中完成特定任务&#xff0c;为人…

linux内核 - 内存分配机制介绍

在linux内核中&#xff0c;下面这张图说明了系统中存在一个可以满足各种内存请求的分配机制。根据你需要内存的用途&#xff0c;你可以选择最接近你目标的分配方式。最底层、最基础的分配器是 页分配器&#xff08;page allocator&#xff09;&#xff0c;它以页为单位分配内存…