使用 go 关键字可以开启一个新的 goroutine 去运行函数
但是go关键字的调用协议与普通函数的调用协议是不同的,如果新的 goroutine 与当前 goroutine 共用一个栈,会导致相互覆盖
正常的函数调用
MOVL $1, 0(SP)
MOVL $2, 4(SP)
MOVL $3, 8(SP)
CALL f(SB)
go f(1, 2, 3) 调用
MOVL $1, 0(SP)
MOVL $2, 4(SP)
MOVL $3, 8(SP)
PUSHQ $f(SB)
PUSHQ $12
CALL runtime.newproc(SB)
POPQ AX
POPQ AX
并没有直接CALL调用函数f,而是将12与函数f的入口地址压栈,然后调用函数runtime.newproc
12是参数占用大小,runtime.newproc函数接受的参数分别是参数大小,新的 goroutine 要运行的函数,函数的n个参数TODO现在源码好像是没有最后这个args的
在runtime.newproc中,会新建一个栈空间,将栈参数的12个字节拷贝到新的栈空间并让栈指针指向参数。
这时的线程状态像被调度器剥夺cpu后一样,寄存器都保存在类似pcb中的一个结构体struct G中,f被保存在struct G的entry域,后面调度器恢复 goroutine 后,新线程从f开始
可以说,go关键字的实现只是一个语法糖衣
go f(args)
等同于
runtime.newproc(size, f, args)