我们都知道goroutine的在golang中发挥了很大的作用,那么当我们创建一个新的goroutine时,它是怎么一步一步创建的呢?都经历了哪些操作呢?今天我们通过源码来剖析一下创建goroutine都经历了些什么?go version 1.15.6
对goroutine最关键的两个函数是 [newproc()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3535-L3564)
和 [newproc1()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3566-L3674)
,而 newproc1()
函数是我们最需要关注的。
函数 newproc()
我们先看一个简单的创建goroutine的例子,找出来创建它的函数。
package main
func start(a, b, c int64) {
_ = a + b + c
}
func main() {
go start(7, 2, 5)
}
输出结果:
➜ gotest go tool compile -S main.go
"".start STEXT nosplit size=1 args=0x18 locals=0x0
0x0000 00000 (main.go:3) TEXT "".start(SB), NOSPLIT|ABIInternal, $0-24
0x0000 00000 (main.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:5) RET
0x0000 c3 .
"".main STEXT size=98 args=0x0 locals=0x30
0x0000 00000 (main.go:7) TEXT "".main(SB), ABIInternal, $48-0
0x0000 00000 (main.go:7) MOVQ (TLS), CX
0x0009 00009 (main.go:7) CMPQ SP, 16(CX)
0x000d 00013 (main.go:7) PCDATA $0, $-2
0x000d 00013 (main.go:7) JLS 90
0x000f 00015 (main.go:7) PCDATA $0, $-1
0x000f 00015 (main.go:7) SUBQ $48, SP
0x0013 00019 (main.go:7) MOVQ BP, 40(SP)
0x0018 00024 (main.go:7) LEAQ 40(SP), BP
0x001d 00029 (main.go:7) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (main.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (main.go:8) MOVL $24, (SP)
0x0024 00036 (main.go:8) LEAQ "".start·f(SB), AX
0x002b 00043 (main.go:8) MOVQ AX, 8(SP)
0x0030 00048 (main.go:8) MOVQ $7, 16(SP)
0x0039 00057 (main.go:8) MOVQ $2, 24(SP)
0x0042 00066 (main.go:8) MOVQ $5, 32(SP)
0x004b 00075 (main.go:8) PCDATA $1, $0
0x004b 00075 (main.go:8) CALL runtime.newproc(SB)
0x0050 00080 (main.go:9) MOVQ 40(SP), BP
0x0055 00085 (main.go:9) ADDQ $48, SP
0x0059 00089 (main.go:9) RET
0x005a 00090 (main.go:9) NOP
0x005a 00090 (main.go:7) PCDATA $1, $-1
0x005a 00090 (main.go:7) PCDATA $0, $-2
0x005a 00090 (main.go:7) CALL runtime.morestack_noctxt(SB)
0x005f 00095 (main.go:7) PCDATA $0, $-1
0x005f 00095 (main.go:7) NOP
0x0060 00096 (main.go:7) JMP 0
0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 4b 48 eH..%....H;a.vKH
0x0010 83 ec 30 48 89 6c 24 28 48 8d 6c 24 28 c7 04 24 ..0H.l$(H.l$(..$
0x0020 18 00 00 00 48 8d 05 00 00 00 00 48 89 44 24 08 ....H......H.D$.
0x0030 48 c7 44 24 10 07 00 00 00 48 c7 44 24 18 02 00 H.D$.....H.D$...
0x0040 00 00 48 c7 44 24 20 05 00 00 00 e8 00 00 00 00 ..H.D$ .........
0x0050 48 8b 6c 24 28 48 83 c4 30 c3 e8 00 00 00 00 90 H.l$(H..0.......
0x0060 eb 9e ..
rel 5+4 t=17 TLS+0
rel 39+4 t=16 "".start·f+0
rel 76+4 t=8 runtime.newproc+0
rel 91+4 t=8 runtime.morestack_noctxt+0
go.cuinfo.packagename. SDWARFINFO dupok size=0
0x0000 6d 61 69 6e main
""..inittask SNOPTRDATA size=24
0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0010 00 00 00 00 00 00 00 00 ........
"".start·f SRODATA dupok size=8
0x0000 00 00 00 00 00 00 00 00 ........
rel 0+8 t=1 "".start+0
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
0x0000 01 00 00 00 00 00 00 00 ........
通过使用 go tool compile -S main.go
命令输出的汇编我们基本可以看到有一个 [runtime.newproc()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3535-L3564)
函数的调用(CALL
是汇编调用函数指令) ,这个正是runtime
创建goroutine
的关键函数。