Go
Язык программирования
часть 1 - запуск
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>