初始化根据平台的不同而不同, 这里参考 adm64 的汇编. 入口参考 runtime#asm_amd64.s, _rt0_amd64(SB)、main(SB)、_rt0_amd64_lib(SB) 分别对应了 内部链接、外部连接以及共享库的三种不同的启动方式. 但是, 最终都会调用 rt0_go.

TEXT runtime·rt0_go(SB),NOSPLIT,$0
    // copy arguments forward on an even stack
    MOVQ    DI, AX      // argc
    MOVQ    SI, BX      // argv
    SUBQ    $(4*8+7), SP        // 2args 2auto
    ANDQ    $~15, SP
    MOVQ    AX, 16(SP)
    MOVQ    BX, 24(SP)
    
    // create istack out of the given (operating system) stack.
    // _cgo_init may update stackguard.
    MOVQ    $runtime·g0(SB), DI
    LEAQ    (-64*1024+104)(SP), BX
    MOVQ    BX, g_stackguard0(DI)
    MOVQ    BX, g_stackguard1(DI)
    MOVQ    BX, (g_stack+stack_lo)(DI)
    MOVQ    SP, (g_stack+stack_hi)(DI)

    .... 
ok:
    // set the per-goroutine and per-mach "registers"
    get_tls(BX)
    LEAQ    runtime·g0(SB), CX
    MOVQ    CX, g(BX)
    LEAQ    runtime·m0(SB), AX

    // save m->g0 = g0
    MOVQ    CX, m_g0(AX)
    // save m0 to g0->m
    MOVQ    AX, g_m(CX)

    CLD             // convention is D is always left cleared
    CALL    runtime·check(SB)

    MOVL    16(SP), AX      // copy argc
    MOVL    AX, 0(SP)
    MOVQ    24(SP), AX      // copy argv
    MOVQ    AX, 8(SP)
    CALL    runtime·args(SB)
    CALL    runtime·osinit(SB)
    CALL    runtime·schedinit(SB)

    // create a new goroutine to start program
    MOVQ    $runtime·mainPC(SB), AX     // entry
    PUSHQ   AX
    PUSHQ   $0          // arg size
    CALL    runtime·newproc(SB)
    POPQ    AX
    POPQ    AX

    // start this M
    CALL    runtime·mstart(SB)

    CALL    runtime·abort(SB)   // mstart should never return
    RET

    // Prevent dead-code elimination of debugCallV1, which is
    // intended to be called by debuggers.
    MOVQ    $runtime·debugCallV1(SB), AX
    RET

DATA    runtime·mainPC+0(SB)/8,$runtime·main(SB) // runtime·mainPC指向了 runtime.main, 也就是第一个goroutine

主要执行了下面几件事情:

CALL    runtime·args(SB)   // 命令行参数传递, 参考 runtime1.go  
CALL    runtime·osinit(SB) // 平台相关, 获取cpu数目, physPageSize, 参考os_freebsd.go 
CALL    runtime·schedinit(SB) // 负责了 栈 、内存分配和垃圾回收、 并发等的初始化
MOVQ    $runtime·mainPC(SB), AX // 指出了第一只goroutine 
CALL    runtime·newproc(SB) // 启动新的goroutine 
CALL    runtime·mstart(SB) // schedule: 发现可运行的goroutine并进行执行, 循环永远不返回 

这里面, 重点是 mstart, 通过后面的 golang调度器分析可以知道, schedule是通过自旋线程来避免频繁的线程切换的, 并保证至少一个自旋线程->意味着至少有一个空闲线程可以用来处理任务, 初始化的时候, 只有一个线程, 当准备进行goroutine执行的时候, 就会创建新的线程. 参看方法 #resetspinning->#wakep->#startm, 通过代码发现, 想要仅仅创建自旋m的话, 还需要有p. 创建m的方法参看 #newm

这里面, newproc 也是golang内部创建 goroutine的函数入口.

第一个执行的goroutine: runtime#main, 负责了 runtime_init、gcenable、用户main函数入口. 其中 runtime_init 通过看连接可以知道, 实际上是启动了新的goroutine进行 forcegchelper, 通过定时器触发gc操作. gcenable 和gc有关, 启动新的goroutine进行bgsweep