一些关于调度的疑问

一些关于调度的疑问

  1. 在程序启动mstart函数中,runtime.main执行main.main,如果main.main直接返回了,那么是不是不会进入到调度循环了?在runtime.mstart1中,main.main执行完了之后才会进入到schedule循环中。
  2. ppt中说的main.main中有调度循环,是因为上面多了一个defer么?所以他根本走不到main结束,直接就去schedule了?
  3. 如果main.main中有协程产生并且阻塞,我们会开一个新的m去运行产生的协程,那么这个新的m的调度就跟m0的调度不是一样的了吧?
  4. 在mstart1中,如果_g_.m != &m , 会将_g_.m.nextp和 m关联起来,为什么?
  5. 为什么mstart中不是用g0和m0去执行main.main,而是新建的一个g呢?

正在回答

登陆购买课程后可参与讨论,去登陆

4回答
  1. 在程序启动mstart函数中,runtime.main执行main.main,如果main.main直接返回了,那么是不是不会进入到调度循环了?在runtime.mstart1中,main.main执行完了之后才会进入到schedule循环中。


    1. 你的意思是 main.main 执行很短一段时间就退出了吧,这种情况下,runtime.main 里的 f() 会返回,然后一路走到最后,整个进程就退出了

    2. main.main 得碰到能接管的阻塞的时候,就是第一课 ppt 里那几种情况,m0 就会把这个 main goroutine 给 park 起来,然后重新进入调度循环。

    3. main.main 如果执行完了,那后面直接调用 exit,整个进程就退出了,和调度循环啥的也没啥关系了

  2. ppt中说的main.main中有调度循环,是因为上面多了一个defer么?所以他根本走不到main结束,直接就去schedule了?


    1. ppt 那里的意思是 m0 会负责执行 main.main,然后如果 main.main 有可接管的阻塞,后面 m0 是可以进调度循环的,进了 schedule 以后其实就和普通的线程没啥区别了~

    2. 第一课直播讲的时候的图稍微有点问题~

  3. 如果main.main中有协程产生并且阻塞,我们会开一个新的m去运行产生的协程,那么这个新的m的调度就跟m0的调度不是一样的了吧?


    1. main.main 本身就是在 main goroutine 里执行的,他阻塞的话,和其它 goroutine 阻塞没啥区别~

    2. 如果是我们之前说的可接管的阻塞,那 m0 可以直接进调度循环去执行在 main 函数里创建的 goroutine~

    3. 如果 main.main 的阻塞是 syscall 那种不可接管的,那 m0 就暂时被占用了,确实需要启动其它的 m 去执行调度

    4. 进调度循环的线程都是一视同仁的,只不过 m0 只是初始线程,这个我印象中是操作系统帮我们创建的(就是其它语言里的主线程),除 m0 以外的其它的线程都是在 Go 里面主动调用 syscall.Clone 创建的

  4. 在mstart1中,如果_g_.m != &m , 会将_g_.m.nextp和 m关联起来,为什么?

    1. 这里我看了看代码,就是启动新线程的时候,newm -> startm 是已经把这个 m 应该绑的 p 设置在 m.nextp 里了。。。到了 mstart 过程只是绑定一下,好像也没看出什么特殊的


  5. 为什么mstart中不是用g0和m0去执行main.main,而是新建的一个g呢?

    1. g0 在所有 m 里都是一个特殊的 g,只能用来执行 runtime 里的调度函数,在执行过程中不能影响到用户的运行栈,一般是 system_stack 或者 mcall 的时候会进去

    2. 对于 runtime 来说,main.main 也是用户代码(runtime.main 也是),所有用户代码都应该在专门的 user g 里执行

  • tan_beta 提问者 #1

    for q4:  为什么不直接将其绑定呢?而是要经过nextp中转一次呢?

    for q5:  在mstart中,进去得_g_就是g0,何时将其切换为普通g得呢?在相应的代码里面没看到显示调用system_stack 或者 mcall 呢。

    q6:在runtime.main里面,最后

    for {
      var x *int32
      *x = 0
    }

    这段代码有什么作用呢(我记得有人好像在微信问过。。尴尬)?


    2021-06-23 08:58:19
  • Xargin 回复 提问者 tan_beta #2

    for q4:  为什么不直接将其绑定呢?而是要经过nextp中转一次呢?

    这里我估计是老线程和新线程之间可能不太方便传这个 p 参数,所以只能通过 m 上的成员来转一下;


    for q5:  在mstart中,进去得_g_就是g0,何时将其切换为普通g得呢?在相应的代码里面没看到显示调用system_stack 或者 mcall 呢。

    在调度循环里 gogo,会切到用户 g 去执行


    q6:在runtime.main里面...

    这种好像是老 unix 的传统,相当于 unreachble 的代码写个死循环能比较快的发现代码的 bug。

    我记得很早的 linux 内核的 panic 就是这样的(个人意见)


    2021-06-23 18:13:25
  • Xargin #3

    fix : 这种好像是老 unix 的传统,相当于 unreachble 的代码 panic 能快速发现 bug

    2021-06-23 21:32:43
Xargin 2021-06-23 00:50:27

看代码的时候需要知道啥时候会切栈,

要看 system_stack 和 mcall,以及 gogo 之类的代码


这几个好像都是汇编实现的,不过注释写清楚了

提问者 tan_beta 2021-06-22 23:01:03

main中不是只有

runningPanicDefers != 0

时,才能进入到调度循环么?我看这个

runningPanicDefers

的注释是说 当要退出时,另外一个协程发生了panic,才会不为0。那么这里main里面还有其他地方进去到调度循环么?

  • Xargin #1

    执行调度循环的是线程,不是 goroutine..

    2021-06-23 00:51:18
提问者 tan_beta 2021-06-22 22:38:33

第五个问题是因为调度需要切换g0去进行调度循环么?

  • Xargin #1

    对的,g0 主要就是用来执行调度循环的~

    2021-06-23 00:51:52
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师