go 程序启动过程(bootstrap sequence)

210 阅读2分钟
// The bootstrap sequence is:
//
// call osinit
// call schedinit
// make & queue new G to run runtime·main(put g in a runnable queue)
// call runtime·mstart (this calls shedule() and The new G get scheduled and calls runtime·main.)

/usr/local/go/src/runtime/asm_amd64.s (golang.design/go-question…)

TEXT runtime·rt0_go(SB),NOSPLIT,$0

    //关联m0和g0
    m0.g0 = &g0
    g0.m = &m0

    // 初始化系统核心数
    CALL    runtime·osinit(SB)
    // 调度器初始化
    CALL    runtime·schedinit(SB)
    

    // create a new goroutine to start program
    MOVQ   $runtime·mainPC(SB), AX       // entry
    //mainPC is a function value for runtime.main, to be passed to newproc.
    PUSHQ  AX
    CALL   runtime·newproc(SB)
    //newproc:Create a new g running fn.  put g on the local runnable queue. 
    //所以这儿是创建了一个g来运行runtime.main
    POPQ   AX

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

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

osint

ncpu = getproccount()
physHugePageSize = getHugePageSize()

schedinit

--->各种lockInit

--->sched.maxmcount = 10000

--->stackinit()//stackpool,stackLarge 的 mSpanList 初始化

--->mallocinit()

--->goargs()、goenvs()、parsedebugvars()

--->gcinit()//初始化 gcPercent , GC pacer state

--->procresize(procs)//创建 GOMAXPROCS or ncpu 个 p

runtime·mstart

mstart0

--->Initialize stack bounds (if osStack)//8192 byte

--->Initialize stack guard

--->调用mstart1()

--->mexit(osStack)

mstart1

--->minit(), Called to initialize a new m ,including the bootstrap m,主要调用 minitSignalStack.

--->mstartm0(),Install signal handlers (if g.m == &m0)

--->执行 g.m.mstartfn,mstartfn有sysmon和templateThread image.png

--->acquirep(g.m.nextp.ptr()),Associate p and the current m.(if g.m != &m0)

--->schedule() //One round of scheduler: find a runnable goroutine and execute it. Never returns.

runtime·main

--->设置maxstacksize,maxstackceiling//Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.

--->newm(sysmon, nil, -1)// 在systemstack中创建监控线程

--->doInit(&runtime_inittask)//runtime 包初始化

--->gcenable()//go bgsweep(c), go bgscavenge(c)

--->doInit(&main_inittask)//main 包初始化

--->调用main.mian

--->exit(0)//退出进程

sysmon

在for循环里面执行:

--->sleep 20us - 10ms

--->poll network if not polled for more than 10ms, non-blocking // netpoll checks for ready network connections. // Returns list of goroutines that become runnable. // add these goroutines to some run queue

--->scavenge.sysmonWake!=0时 wakeScavenger //add scavenge.g to some run queue

--->Preempt G if it's running for too long && Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us).

--->check if we need to force a GC //add forcegc.g to some run queue, 由 runtime.forcegcperiod 变量控制,默认为2 分钟

preemptone

// Every call in a goroutine checks for stack overflow by
// comparing the current stack pointer to gp->stackguard0.
// Setting gp->stackguard0 to StackPreempt folds
// preemption into the normal stack overflow check.
gp.stackguard0 = stackPreempt

handoffp

retake 调用 handoffp // Hands off P from syscall or locked M.

handoffp 里面主要调用 startm

startm(_p_, false)//`Schedules some M to run the p (creates an M if necessary).

injectglist

// injectglist adds each runnable G on the list to some run queue,
// and clears glist. If there is no current P, they are added to the
// global queue, and up to npidle M's are started to run them.
// Otherwise, for each idle P, this adds a G to the global queue
// and starts an M. Any remaining G's are added to the current P's
// local run queue.
// This may temporarily acquire sched.lock.
// Can run concurrently with GC.
func injectglist(glist *gList) {
...
}