前面几篇,我们进行了从程序入口开始到m0 g0 的初始化,全局变量allm allp 的维护,到newg的生成,通过栈帧操作插入exit到函数栈顶,将newg加入p的local runqueue,开启调度循环schedule()最后gogo 寄存器上下文切换 开始进入newg的执行~
这里有一小段我们是简单跳过的,这里用这张图说明一下,因为golang的启动涉及到各模块的初始化,后面我们分析gc,内存分配,类型系统等都需要基于这里。
这张图把schedinit之前我们跳过的部分,说明了一下。主要涉及到
1、stackpool 栈内存分配初始化
2、malloc 堆内存分配初始化 mheap,heapArena等
3、类型系统初始化 itabinit() typeslinkinit()
4、gc初始化
这个我们之后分析到对应模块的时候再分析
开始今天的正题吧
runtime.main
简单一点用个流程图画一下:
exit(0)不过,main goroutine 实际上就是代表用户的 main 函数,它都执行完了,肯定是用户的任务都执行完了,直接退出就可以了,就算有其他的 goroutine 没执行完,同样会直接退出。
所以各位在进行go func的时候要注意waitgroup的使用哦, main退出了进程也退出了,over~
看到这里可能有人要问了?之前不是在main.main的栈帧前面插入了goexit嘛~那main goroutine通过exit(0)直接退出进程,是不是代表这个操作没啥用???
goexit()的作用
既然maingoroutine起不了作用,那就是其他的goroutine需要了~我们还是直接看代码 runtime·goexit1
没几行代码哈~直接到mcall(goexit0)
又是一个汇编函数 mcall
不多解释了 主要就干了切换到g0 然后开始执行fn() 也就是goexit0
了解过Funcation Value的肯定知道 fn地址指向函数的指令入口
最后看下goexit0
修改运行状态,清空g字段,解绑g和m之间的关系,保存到gFree等待下次复用,调用schedule()再次进行调度。