Golang中runtime.schedule()如何获取p和parallelism

Golang中runtime.schedule()如何获取p和parallelism 你好!根据源代码,schedule() 函数代表一轮调度。 从这里开始,我们寻找本地 p 队列(有时为了公平性也会从 sched 的全局队列中寻找),然后找到可运行的 g 并执行它。 这个函数如何知道在哪个 p 内部执行搜索?启动这个函数的顺序是什么?它是由每个附加到 p 的 m 对应的多个操作系统线程启动的吗?

1 回复

更多关于Golang中runtime.schedule()如何获取p和parallelism的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go调度器的runtime.schedule()函数中,获取当前P和并行度(parallelism)的机制如下:

获取当前P(Processor)

schedule()函数中,通过getg().m.p.ptr()获取当前M绑定的P:

// runtime/proc.go
func schedule() {
    // 获取当前goroutine
    gp := getg()
    
    // 通过当前goroutine的M获取绑定的P
    _g_ := getg()
    _p_ := _g_.m.p.ptr()  // 获取当前M绑定的P
    
    // 如果没有绑定P,则尝试获取一个
    if _p_ == nil {
        // 尝试从空闲P列表获取或窃取工作
    }
    
    // 后续调度逻辑使用_p_进行本地队列搜索
}

获取并行度(parallelism)

并行度通过gomaxprocs变量获取,该变量在运行时初始化时设置:

// runtime/proc.go
func schedule() {
    // 获取GOMAXPROCS值(最大并行度)
    procs := gomaxprocs
    
    // 或者通过全局sched获取
    sched := &schedinit
    maxprocs := sched.maxprocs
}

调度启动顺序

  1. 初始化阶段
// runtime/proc.go
func schedinit() {
    // 设置GOMAXPROCS
    procs := ncpu
    if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
        procs = n
    }
    
    // 创建P数组
    allp = make([]*p, procs)
    
    // 初始化每个P
    for i := 0; i < procs; i++ {
        pp := allp[i]
        // 初始化P的本地队列等
    }
}
  1. M与P绑定
// 每个M在执行schedule()前需要绑定P
func acquirep(pp *p) {
    // 将P绑定到当前M
    getg().m.p.set(pp)
    pp.m.set(getg().m)
    pp.status = _Prunning
}
  1. 调度循环
// 每个M的执行循环
func mstart() {
    // 系统线程入口
    // ...
    schedule()
}

// schedule()被每个M循环调用
func schedule() {
    // 获取当前M绑定的P
    _p_ := getg().m.p.ptr()
    
    // 调度逻辑:从_p_的本地队列、全局队列或其他P窃取工作
    // ...
    
    // 执行找到的goroutine
    execute(gp)
}

执行模型示例

// 简化的调度循环示意
func schedulerLoop(m *m) {
    // M需要先绑定P
    if m.p == 0 {
        // 尝试获取空闲P或创建新P
        p := pidleget()
        if p == nil {
            p = procresize()
        }
        acquirep(p)
    }
    
    // 调度循环
    for {
        schedule()  // 每轮调度
        
        // schedule()内部:
        // 1. 通过getg().m.p.ptr()获取当前P
        // 2. 从该P的本地队列获取G
        // 3. 如果本地队列为空,从全局队列或其他P窃取
        // 4. 执行找到的G
    }
}

关键点

  1. P的获取:每个M通过getg().m.p.ptr()获取当前绑定的P
  2. 并行度控制gomaxprocs决定同时执行的最大P数量
  3. 调度触发:每个操作系统线程(M)独立运行调度循环,但必须绑定P才能执行Go代码
  4. 工作窃取:当本地P队列为空时,会尝试从全局队列或其他P窃取工作

这种设计确保了在GOMAXPROCS限制内的真正并行执行,同时通过工作窃取机制保持负载均衡。

回到顶部