package main import "fmt" func main() { fmt.Println("Hello World") }
Собираем и смотрим на результат выполнения
$ go build hello.go $ ./hello Hello World
$ objdump -f hello hello: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x0000000000421700
$ objdump -d hello|grep -A 10 \ > $(objdump -f hello|grep "start"|egrep -o '[0-9]+$') 0000000000421700 <_rt0_amd64_linux>: 421700: 48 8d 74 24 08 lea 0x8(%rsp),%rsi 421705: 48 8b 3c 24 mov (%rsp),%rdi 421709: b8 10 17 42 00 mov $0x421710,%eax 42170e: ff e0 jmpq *%rax 0000000000421710 <main>: 421710: b8 70 f9 41 00 mov $0x41f970,%eax 421715: ff e0 jmpq *%rax ...
TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8 LEAQ 8(SP), SI // argv MOVQ 0(SP), DI // argc MOVQ $main(SB), AX JMP AX TEXT main(SB),NOSPLIT,$-8 MOVQ $_rt0_go(SB), AX JMP AX
Дынне переданные системному вызову sys_execve лежат в регистрах - сохраняем их на стеке и вызываем _rt0_go
CALL runtime·args(SB) CALL runtime·osinit(SB) CALL runtime·hashinit(SB) CALL runtime·schedinit(SB) // create a new goroutine to start program PUSHL $runtime·main·f(SB) // entry PUSHL $0 // arg size ARGSIZE(8) CALL runtime·newproc(SB) ARGSIZE(-1) POPL AX POPL AX // start this M CALL runtime·mstart(SB)
Состояние до вызова schedule()
(gdb) i goproc P:0 running schedtick=0 syscalltick=0 gfreecnt=0 runq[128] o G:1 runnable runtime.main ... (gdb) i gothreads M:0 procid=0 curg=<o G:null> p=<P:0>
Достает из очереди процессора ожидающую выполенение горутину и запускает ее посредством вызова execute
static void schedule(void) { ... if(gp == nil) { gp = runqget(m->p); if(gp && m->spinning) runtime·throw("schedule: spinning with local work"); } ... execute(gp); }
static void execute(G *gp) { ... gp->status = Grunning; gp->preempt = false; gp->stackguard0 = gp->stackguard; m->p->schedtick++; m->curg = gp; gp->m = m; ... runtime·gogo(&gp->sched); }
Состояние перед вызовом runtime·gogo
(gdb) i gothreads M:0 procid=0 curg=<* G:1 running runtime.main m=<M:0>> p=<P:0> (gdb) i goproc P:0 running schedtick=1 syscalltick=0 gfreecnt=0 runq[128] ... (gdb) i goroutines * G:1 running runtime.main m=<M:0>
// void gogo(Gobuf*) // restore state from Gobuf; longjmp TEXT runtime·gogo(SB), NOSPLIT, $0-8 MOVQ 8(SP), BX // gobuf MOVQ gobuf_g(BX), DX MOVQ 0(DX), CX // make sure g != nil get_tls(CX) MOVQ DX, g(CX) MOVQ gobuf_sp(BX), SP // restore SP MOVQ gobuf_ret(BX), AX MOVQ gobuf_ctxt(BX), DX MOVQ $0, gobuf_sp(BX) // clear to help garbage collector MOVQ $0, gobuf_ret(BX) MOVQ $0, gobuf_ctxt(BX) MOVQ gobuf_pc(BX), BX JMP BX
// The main goroutine. void runtime·main(void) { .. newm(sysmon, nil); ... runtime·newproc1(&scavenger, nil, 0, 0, runtime·main); main·init(); ... main·main(); ... runtime·exit(0); for(;;) *(int32*)runtime·main = 0; }
Состояние к моменту вызова main·main
(gdb) i gothreads M:3 procid=27165 curg=<o G:null> p=<P:null> M:2 procid=27164 curg=<o G:null> p=<P:null> M:1 procid=27108 curg=<o G:null> p=<P:null> M:0 procid=0 curg=<* G:1 running runtime.unlockOSThread m=<M:0>> p=<P:0> (gdb) i goproc P:0 running schedtick=5 syscalltick=0 gfreecnt=0 runq[128] o G:2 runnable runtime.exitsyscall m=<M:null> ... (gdb) i goroutines * G:1 running runtime.unlockOSThread m=<M:0> o G:2 runnable runtime.exitsyscall m=<M:null>
$ pstree -pa 26430 gdb,26430 example1 └─example1,26485 ├─{example1},27108 ├─{example1},27164 └─{example1},27165
К моменту завершения программы
(gdb) i gothreads M:3 procid=27165 curg=<o G:null> p=<P:null> M:2 procid=27164 curg=<o G:null> p=<P:null> M:1 procid=27108 curg=<o G:null> p=<P:null> M:0 procid=0 curg=<* G:1 running syscall.Syscall m=<M:0>> p=<P:0> (gdb) i goproc P:0 running schedtick=5 syscalltick=1 gfreecnt=0 runq[128] o G:2 runnable runtime.exitsyscall m=<M:null> ... (gdb) i goroutines * G:1 running syscall.Syscall m=<M:0> o G:2 runnable runtime.exitsyscall m=<M:null>