Go程序的“一生”

Go程序的“一生”

作者:好奇还思猫&王逗比

版权:未经允许不能转载


相信大多数人对于Go程序从代码到编译,再到执行的过程很好奇,但是又每次不得而知。我们来扒一扒这个过程。

(控制流图来自南大编译器安全研究僧好基友王逗比)

准备

操作系统:Mac 10.11.2 (15C50)

Go:1.8.3

GDB:8.0

angr && IDA


初探

首先我们从最简单的hello,world开始:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello,world")
}                            

编译成可执行文件,

go build -gcflags "-N -l" hello.go

参数 "-N -l":关闭内联优化

然后使用GDB我们来看看这个过程

给下列函数下断点,方便我们查看这个过程:

_rt0_amd64_darwin
main
runtime.check
runtime.args
runtime.osinit
runtime.schedinit
runtime.newproc
runtime.mstart
main.main
runtime.exit

然后运行我们可以看到:

gdb) r
Starting program: /Users/wangguoliang/Desktop/hello
// 整个程序启动是从_rt0_amd64_darwin开始,它将参数(argc 与 argv )保存到寄存器
Breakpoint 1, _rt0_amd64_darwin () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/rt0_darwin_amd64.s:8
8		LEAQ	8(SP), SI // argv
(gdb) c
Continuing.
// 然后到main() 
Breakpoint 2, main () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/rt0_darwin_amd64.s:78
78		MOVQ	$runtime·rt0_go(SB), AX
(gdb)
Continuing.
// runtime 检查当前运行环境和一些初始化工作
Breakpoint 3, runtime.check () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/runtime1.go:141
141	func check() {
(gdb)
Continuing.
// 处理args参数
Breakpoint 4, runtime.args (c=1, v=0x7fff5fbff9c8) at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/runtime1.go:61
61	func args(c int32, v **byte) {
(gdb)
Continuing.
// osinit进行初始化,系统设置信息,例如GC,CGO等等
Breakpoint 5, runtime.osinit () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/os_darwin.go:48
48	func osinit() {
(gdb)
Continuing.
// 调度起初始化,包含有调用mallocinit,mcommoninit分别对内存管理模块初始化,对当前的结构体M初始化,同时在此也会
// 根据环境变量GOMAXPROCS决定可用物理线程数;将程序的main函数参数argc和argv等复制到了os.Args中.
Breakpoint 6, runtime.schedinit () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:462
462	func schedinit() {
(gdb)
Continuing.
// 新开个goroutine执行runtime.main
Breakpoint 7, runtime.newproc (siz=0, fn=0x10bb500 <runtime.mainPC>)
    at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:2841
2841	func newproc(siz int32, fn *funcval) {
(gdb)
Continuing.
// 开启M,即工作线程
Breakpoint 8, runtime.mstart () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1132
1132	func mstart() {
(gdb)
Continuing.
[New Thread 0x1353 of process 6127]
[Switching to Thread 0x1353 of process 6127]

Thread 2 hit Breakpoint 8, runtime.mstart () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1132
1132	func mstart() {
(gdb)
Continuing.
[Switching to Thread 0x1103 of process 6127]

Thread 1 hit Breakpoint 7, runtime.newproc (siz=0, fn=0x10b7d08 <func.*+518>)
    at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:2841
2841	func newproc(siz int32, fn *funcval) {
(gdb)
Continuing.
[New Thread 0x1413 of process 6127]
[New Thread 0x1507 of process 6127]
[Switching to Thread 0x1413 of process 6127]

Thread 3 hit Breakpoint 8, runtime.mstart () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1132
1132	func mstart() {
(gdb)
Continuing.
[New Thread 0x1707 of process 6127]
[Switching to Thread 0x1507 of process 6127]

Thread 4 hit Breakpoint 8, runtime.mstart () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1132
1132	func mstart() {
(gdb)
Continuing.
[Switching to Thread 0x1707 of process 6127]

Thread 5 hit Breakpoint 8, runtime.mstart () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1132
1132	func mstart() {
(gdb)
Continuing.
[Switching to Thread 0x1103 of process 6127]
/
Thread 1 hit Breakpoint 7, runtime.newproc (siz=8, fn=0x10b7c58 <func.*+342>)
    at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:2841
2841	func newproc(siz int32, fn *funcval) {
(gdb)
Continuing.

Thread 1 hit Breakpoint 7, runtime.newproc (siz=0, fn=0x10b7ea0 <func.*+926>)
    at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:2841
2841	func newproc(siz int32, fn *funcval) {
(gdb)
Continuing.
// runtime.main会调用用户的main函数,即main.main从此进入用户代码,输出hello,world
Thread 1 hit Breakpoint 9, main.main () at /Users/wangguoliang/Desktop/hello.go:7
7	func main() {
(gdb)
Continuing.
hello,world
// 退出程序
Thread 1 hit Breakpoint 10, runtime.exit () at /usr/local/Cellar/go/1.8.3/libexec/src/runtime/sys_darwin_amd64.s:20
20		MOVL	code+0(FP), DI		// arg 1 exit status
(gdb)
Continuing.
[Inferior 1 (process 6127) exited normally]

一共开启了6个M,如果期间你运行info goroutines命令,你会发现共开启了11种不同的goroutines状态

  • runtime.main
  • runtime.init.2
  • runtime.gcenable
  • runtime.forcegchelper
  • runtime.systemstack_switch
  • runtime.gopark
  • runtime.chanrecv
  • runtime.bgsweep
  • runtime.systemstack_switch
  • syscall.Syscall6
  • syscall.Syscall

梳理

下面我们梳理下这个流程:

1.启动是从_rt0_amd64_darwin开始,在不同系统有着不同的文件,

/src/runtime/rt0_android_386.s

/src/runtime/rt0_android_amd64.s

/src/runtime/rt0_android_arm.s

/src/runtime/rt0_android_arm64.s

/src/runtime/rt0_darwin_386.s

/src/runtime/rt0_darwin_amd64.s

/src/runtime/rt0_darwin_arm.s

/src/runtime/rt0_darwin_arm64.s

/src/runtime/rt0_dragonfly_amd64.s

/src/runtime/rt0_freebsd_386.s

/src/runtime/rt0_freebsd_amd64.s

/src/runtime/rt0_freebsd_arm.s

/src/runtime/rt0_linux_386.s

/src/runtime/rt0_linux_amd64.s

/src/runtime/rt0_linux_arm.s

/src/runtime/rt0_linux_arm64.s

/src/runtime/rt0_linux_mips64x.s

/src/runtime/rt0_linux_mipsx.s

/src/runtime/rt0_linux_ppc64.s

/src/runtime/rt0_linux_ppc64le.s

/src/runtime/rt0_linux_s390x.s

/src/runtime/rt0_nacl_386.s

/src/runtime/rt0_nacl_amd64p32.s

/src/runtime/rt0_nacl_arm.s

/src/runtime/rt0_netbsd_386.s

/src/runtime/rt0_netbsd_amd64.s

/src/runtime/rt0_netbsd_arm.s

/src/runtime/rt0_openbsd_386.s

/src/runtime/rt0_openbsd_amd64.s

/src/runtime/rt0_openbsd_arm.s

/src/runtime/rt0_plan9_386.s

/src/runtime/rt0_plan9_amd64.s

/src/runtime/rt0_plan9_arm.s

/src/runtime/rt0_solaris_amd64.s

/src/runtime/rt0_windows_386.s

/src/runtime/rt0_windows_amd64.s

2.然后进入main(),调用check(),检查当前运行环境和一些初始化工作,处理args,argv等

3.osinit进行初始化,系统设置信息,例如GC,CGO等等

4.调度起初始化,包含有调用mallocinit,mcommoninit分别对内存管理模块初始化,对当前的结构体M初始化,同时在此也会根据环境变量GOMAXPROCS决定可用物理线程数;将程序的main函数参数argc和argv等复制到了os.Args中.

5.新开个goroutine执行runtime.main

6.开启M,即工作线程

7.接下来就是创建一些goroutine来执行gc,调度器,gopark(转换goroutine为wait状态的函数),systemstack_switch做栈切换工作,Syscall做一些系统调用工作。

8.runtime.main会调用用户的main函数,即main.main从此进入用户代码,输出hello,world

9.退出程序


下面我们看看我基友(南大研究僧一吊)生成的图:


从上图可以很清晰看到调用顺序和跳转流,也可以看到其中的汇编码。

(windows下生成的图更加清晰,不过比较大,我这里不再贴了,感兴趣的同学可以自行实验!)

runtime.args


runtime.args 将参数(argc 和 argv )存储到静态变量中之外,在 Linux 系统上时它还会分析 处理 ELF 辅助向量以及初始化系统系统调用的地址。

runtime.check


runtime.mstart


runtime.newprocs

在runtime.newproc中,会新建一个栈空间,将栈参数拷贝到新栈空间中并让栈指针指向参数。这时的线程状态有点像当被调度器剥夺CPU后一样。

runtime.osinit


runtime.schedinit


获取程序运行参数;获取程序环境变量

runtime.settls

深入

在mac下直接生成汇编可以更加明了:

"".main t=1 size=170 args=0x0 locals=0x58
// 这个"". 代表的是这个函数的命名空间。

	0x0000 00000 (hello.go:7)	TEXT	"".main(SB), $88-0
// main(SB) 这里就有个SB的伪寄存器。全名未Static Base,代表main这个函数地址,$88-0中的$88代表局部变量,-0代表
// 在$88的地址基础上空出0的长度作为传入和返回对象。这个也就是golang如何实现函数的多返回值的方法了。它在定义函数的时候,
// 开辟了一定空间存储传入和传出对象。
	0x0000 00000 (hello.go:7)	MOVQ	(TLS), CX
	0x0009 00009 (hello.go:7)	CMPQ	SP, 16(CX)
	0x000d 00013 (hello.go:7)	JLS	160
	0x0013 00019 (hello.go:7)	SUBQ	$88, SP
	0x0017 00023 (hello.go:7)	MOVQ	BP, 80(SP)
	0x001c 00028 (hello.go:7)	LEAQ	80(SP), BP
	0x0021 00033 (hello.go:7)	FUNCDATA	$0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
	0x0021 00033 (hello.go:7)	FUNCDATA	$1, gclocals·adc9a4e9eec70025782f0016681b6ae2(SB)
	// FUNCDATA是golang编译器自带的指令,plan9和x86的指令集都是没有的。它用来给gc收集进行提示。提示0和0和1是用于局部
	// 函数调用参数,需要进行回收。
	0x0021 00033 (hello.go:8)	LEAQ	go.string."hello,world"(SB), AX
	// 字符串hello,world给AX
	0x0028 00040 (hello.go:8)	MOVQ	AX, ""..autotmp_1+48(SP)
	0x002d 00045 (hello.go:8)	MOVQ	$11, ""..autotmp_1+56(SP)
	0x0036 00054 (hello.go:8)	MOVQ	$0, ""..autotmp_0+64(SP)
	0x003f 00063 (hello.go:8)	MOVQ	$0, ""..autotmp_0+72(SP)
	0x0048 00072 (hello.go:8)	LEAQ	type.string(SB), AX
	0x004f 00079 (hello.go:8)	MOVQ	AX, (SP)
	0x0053 00083 (hello.go:8)	LEAQ	""..autotmp_1+48(SP), AX
	0x0058 00088 (hello.go:8)	MOVQ	AX, 8(SP)
	0x005d 00093 (hello.go:8)	PCDATA	$0, $1
	0x005d 00093 (hello.go:8)	CALL	runtime.convT2E(SB)
	0x0062 00098 (hello.go:8)	MOVQ	24(SP), AX
	0x0067 00103 (hello.go:8)	MOVQ	16(SP), CX
	0x006c 00108 (hello.go:8)	MOVQ	CX, ""..autotmp_0+64(SP)
	0x0071 00113 (hello.go:8)	MOVQ	AX, ""..autotmp_0+72(SP)
	0x0076 00118 (hello.go:8)	LEAQ	""..autotmp_0+64(SP), AX
	0x007b 00123 (hello.go:8)	MOVQ	AX, (SP)
	0x007f 00127 (hello.go:8)	MOVQ	$1, 8(SP)
	0x0088 00136 (hello.go:8)	MOVQ	$1, 16(SP)
	0x0091 00145 (hello.go:8)	PCDATA	$0, $1
	0x0091 00145 (hello.go:8)	CALL	fmt.Println(SB)
	// 调用fmt.Println函数
	0x0096 00150 (hello.go:9)	MOVQ	80(SP), BP
	0x009b 00155 (hello.go:9)	ADDQ	$88, SP
	0x009f 00159 (hello.go:9)	RET
	0x00a0 00160 (hello.go:9)	NOP
	// NOP占位
	0x00a0 00160 (hello.go:7)	PCDATA	$0, $-1
	// 包含一些垃圾收集器需要的信息
	0x00a0 00160 (hello.go:7)	CALL	runtime.morestack_noctxt(SB)
	0x00a5 00165 (hello.go:7)	JMP	0
	0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 0f 86 8d  eH..%....H;a....
	0x0010 00 00 00 48 83 ec 58 48 89 6c 24 50 48 8d 6c 24  ...H..XH.l$PH.l$
	0x0020 50 48 8d 05 00 00 00 00 48 89 44 24 30 48 c7 44  PH......H.D$0H.D
	0x0030 24 38 0b 00 00 00 48 c7 44 24 40 00 00 00 00 48  $8....H.D$@....H
	0x0040 c7 44 24 48 00 00 00 00 48 8d 05 00 00 00 00 48  .D$H....H......H
	0x0050 89 04 24 48 8d 44 24 30 48 89 44 24 08 e8 00 00  ..$H.D$0H.D$....
	0x0060 00 00 48 8b 44 24 18 48 8b 4c 24 10 48 89 4c 24  ..H.D$.H.L$.H.L$
	0x0070 40 48 89 44 24 48 48 8d 44 24 40 48 89 04 24 48  @H.D$HH.D$@H..$H
	0x0080 c7 44 24 08 01 00 00 00 48 c7 44 24 10 01 00 00  .D$.....H.D$....
	0x0090 00 e8 00 00 00 00 48 8b 6c 24 50 48 83 c4 58 c3  ......H.l$PH..X.
	0x00a0 e8 00 00 00 00 e9 56 ff ff ff                    ......V...
	rel 5+4 t=16 TLS+0
	rel 36+4 t=15 go.string."hello,world"+0
	rel 75+4 t=15 type.string+0
	rel 94+4 t=8 runtime.convT2E+0
	rel 146+4 t=8 fmt.Println+0
	rel 161+4 t=8 runtime.morestack_noctxt+0
"".init t=1 size=91 args=0x0 locals=0x8
// 默认带有的init函数
	0x0000 00000 (hello.go:10)	TEXT	"".init(SB), $8-0
	0x0000 00000 (hello.go:10)	MOVQ	(TLS), CX
	0x0009 00009 (hello.go:10)	CMPQ	SP, 16(CX)
	0x000d 00013 (hello.go:10)	JLS	84
	0x000f 00015 (hello.go:10)	SUBQ	$8, SP
	0x0013 00019 (hello.go:10)	MOVQ	BP, (SP)
	0x0017 00023 (hello.go:10)	LEAQ	(SP), BP
	0x001b 00027 (hello.go:10)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001b 00027 (hello.go:10)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001b 00027 (hello.go:10)	MOVBLZX	"".initdone·(SB), AX
	0x0022 00034 (hello.go:10)	CMPB	AL, $1
	0x0024 00036 (hello.go:10)	JLS	$0, 47
	0x0026 00038 (hello.go:10)	MOVQ	(SP), BP
	0x002a 00042 (hello.go:10)	ADDQ	$8, SP
	0x002e 00046 (hello.go:10)	RET
	0x002f 00047 (hello.go:10)	JNE	$0, 56
	0x0031 00049 (hello.go:10)	PCDATA	$0, $0
	0x0031 00049 (hello.go:10)	CALL	runtime.throwinit(SB)
	0x0036 00054 (hello.go:10)	UNDEF
	0x0038 00056 (hello.go:10)	MOVB	$1, "".initdone·(SB)
	0x003f 00063 (hello.go:10)	PCDATA	$0, $0
	0x003f 00063 (hello.go:10)	CALL	fmt.init(SB)
	0x0044 00068 (hello.go:10)	MOVB	$2, "".initdone·(SB)
	0x004b 00075 (hello.go:10)	MOVQ	(SP), BP
	0x004f 00079 (hello.go:10)	ADDQ	$8, SP
	0x0053 00083 (hello.go:10)	RET
	0x0054 00084 (hello.go:10)	NOP
	0x0054 00084 (hello.go:10)	PCDATA	$0, $-1
	0x0054 00084 (hello.go:10)	CALL	runtime.morestack_noctxt(SB)
	0x0059 00089 (hello.go:10)	JMP	0
	0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 45 48  eH..%....H;a.vEH
	0x0010 83 ec 08 48 89 2c 24 48 8d 2c 24 0f b6 05 00 00  ...H.,$H.,$.....
	0x0020 00 00 3c 01 76 09 48 8b 2c 24 48 83 c4 08 c3 75  ..<.v.H.,$H....u
	0x0030 07 e8 00 00 00 00 0f 0b c6 05 00 00 00 00 01 e8  ................
	0x0040 00 00 00 00 c6 05 00 00 00 00 02 48 8b 2c 24 48  ...........H.,$H
	0x0050 83 c4 08 c3 e8 00 00 00 00 eb a5                 ...........
	rel 5+4 t=16 TLS+0
	rel 30+4 t=15 "".initdone·+0
	rel 50+4 t=8 runtime.throwinit+0
	rel 58+4 t=15 "".initdone·+-1
	rel 64+4 t=8 fmt.init+0
	rel 70+4 t=15 "".initdone·+-1
	rel 85+4 t=8 runtime.morestack_noctxt+0
go.string."hello,world" t=8 dupok size=11
	0x0000 68 65 6c 6c 6f 2c 77 6f 72 6c 64                 hello,world
gclocals·adc9a4e9eec70025782f0016681b6ae2 t=8 dupok size=10
	0x0000 02 00 00 00 04 00 00 00 00 0d                    ..........
gclocals·69c1753bd5f81501d95132d08af04464 t=8 dupok size=8
	0x0000 02 00 00 00 00 00 00 00                          ........
go.info."".main t=45 size=27
	0x0000 02 22 22 2e 6d 61 69 6e 00 00 00 00 00 00 00 00  ."".main........
	0x0010 00 00 00 00 00 00 00 00 00 01 00                 ...........
	rel 9+8 t=1 "".main+0
	rel 17+8 t=1 "".main+170
gclocals·33cdeccccebe80329f1fdbee7f5874cb t=8 dupok size=8
	0x0000 01 00 00 00 00 00 00 00                          ........
go.info."".init t=45 size=27
	0x0000 02 22 22 2e 69 6e 69 74 00 00 00 00 00 00 00 00  ."".init........
	0x0010 00 00 00 00 00 00 00 00 00 01 00                 ...........
	rel 9+8 t=1 "".init+0
	rel 17+8 t=1 "".init+91
"".initdone· t=32 size=1
runtime.gcbits.01 t=8 dupok size=1
	0x0000 01                                               .
type..namedata.*interface {}. t=8 dupok size=16
	0x0000 00 00 0d 2a 69 6e 74 65 72 66 61 63 65 20 7b 7d  ...*interface {}
type.*interface {} t=8 dupok size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 4f 0f 96 9d 00 08 08 36 00 00 00 00 00 00 00 00  O......6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.algarray+80
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*interface {}.+0
	rel 48+8 t=1 type.interface {}+0
runtime.gcbits.03 t=8 dupok size=1
	0x0000 03                                               .
type.interface {} t=8 dupok size=80
	0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
	0x0010 e7 57 a0 18 02 08 08 14 00 00 00 00 00 00 00 00  .W..............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	rel 24+8 t=1 runtime.algarray+144
	rel 32+8 t=1 runtime.gcbits.03+0
	rel 40+4 t=5 type..namedata.*interface {}.+0
	rel 44+4 t=6 type.*interface {}+0
	rel 56+8 t=1 type.interface {}+80
type..namedata.*[]interface {}. t=8 dupok size=18
	0x0000 00 00 0f 2a 5b 5d 69 6e 74 65 72 66 61 63 65 20  ...*[]interface 
	0x0010 7b 7d                                            {}
type.*[]interface {} t=8 dupok size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 f3 04 9a e7 00 08 08 36 00 00 00 00 00 00 00 00  .......6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.algarray+80
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*[]interface {}.+0
	rel 48+8 t=1 type.[]interface {}+0
type.[]interface {} t=8 dupok size=56
	0x0000 18 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 70 93 ea 2f 02 08 08 17 00 00 00 00 00 00 00 00  p../............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.algarray+0
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*[]interface {}.+0
	rel 44+4 t=6 type.*[]interface {}+0
	rel 48+8 t=1 type.interface {}+0
type..namedata.*[1]interface {}. t=8 dupok size=19
	0x0000 00 00 10 2a 5b 31 5d 69 6e 74 65 72 66 61 63 65  ...*[1]interface
	0x0010 20 7b 7d                                          {}
type.*[1]interface {} t=8 dupok size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 bf 03 a8 35 00 08 08 36 00 00 00 00 00 00 00 00  ...5...6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.algarray+80
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*[1]interface {}.+0
	rel 48+8 t=1 type.[1]interface {}+0
type.[1]interface {} t=8 dupok size=72
	0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
	0x0010 50 91 5b fa 02 08 08 11 00 00 00 00 00 00 00 00  P.[.............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0040 01 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.algarray+144
	rel 32+8 t=1 runtime.gcbits.03+0
	rel 40+4 t=5 type..namedata.*[1]interface {}.+0
	rel 44+4 t=6 type.*[1]interface {}+0
	rel 48+8 t=1 type.interface {}+0
	rel 56+8 t=1 type.[]interface {}+0
type..importpath.fmt. t=8 dupok size=6
	0x0000 00 00 03 66 6d 74                                ...fmt



QA

  1. 一个go程序默认到底是几个goroutines?
  2. go程序入口出口是什么?中间大致需要有哪些准备?
  3. 创建一个goroutine内部发生了什么?与直接调用一个函数有什么区别?


参考资料:


欢迎拍砖,欢迎补充!

编辑于 2017-08-06

文章被以下专栏收录

    GoReporter(业界第一款Go代码质量分析工具)作者;关注Go底层的东西;非常关注Go的最佳实践;代码强迫症,看不下去烂代码;没事读读源码;Go持续学习中