Go

Язык программирования

часть 1 - запуск

Hello World

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
    ...

Точка входа (i386:x86-64)

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

_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)

_rt0_go

_rt0_go

Состояние до вызова 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>

schedule()

Достает из очереди процессора ожидающую выполенение горутину и запускает ее посредством вызова 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);
}

execute()

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);
}

execute()

Состояние перед вызовом 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>

runtime·gogo()

// 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

runtime·main()

// 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;
}

runtime·main()

Состояние к моменту вызова 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

runtime·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 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>