前面几篇,我们进行了从程序入口开始到m0 g0 的初始化,全局变量allm allp 的维护,到newg的生成,通过栈帧操作插入exit到函数栈顶,将newg加入p的local runqueue,开启调度循环schedule()最后gogo 寄存器上下文切换 开始进入newg的执行~

这里有一小段我们是简单跳过的,这里用这张图说明一下,因为golang的启动涉及到各模块的初始化,后面我们分析gc,内存分配,类型系统等都需要基于这里。

golang启动过程流程图

这张图把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()再次进行调度。