Understanding the Go Program Startup Process and Scheduler Initialization
This article comprehensively explains the Go program startup sequence, detailing how the runtime locates the entry point, initializes m0, g0, and P structures, processes command‑line arguments, sets up thread‑local storage, creates the first goroutine, and launches the GMP scheduler.
The article introduces the second part of the Go scheduler series, focusing on the overall startup process of a Go program. It outlines the goals: locating the program entry, describing the initialization steps of m0, g0, and p objects, binding the system thread to m0, creating the first goroutine, and starting the GMP scheduler.
Program entry discovery – When an executable is loaded, the OS reads the binary, creates a process and its main thread, allocates a stack, copies command‑line arguments, and places the thread in the run queue. The actual Go entry point is not runtime.main but the assembly stub rt0_go (e.g., src/runtime/rt0_darwin_amd64.s or src/runtime/rt0_linux_amd64.s ).
Step 1: Receive command‑line arguments – The stub copies argc and argv onto a 16‑byte‑aligned stack and stores them at SP+24 and SP+32 respectively.
TEXT _rt0_amd64(SB),NOSPLIT,$-8
MOVQ 0(SP), DI // argc
MOVQ 8(SP), SI // argv
JMP runtime·rt0_go(SB)Step 2: Initialize g0 stack memory – A 64 KB stack is carved out of the OS stack for the special goroutine g0 , which the runtime uses for its own code.
MOVQ $runtime·g0(SB), DI // DI = &g0
LEAQ (-64*1024+104)(SP), BX // allocate stack
MOVQ BX, g_stackguard0(DI)
MOVQ BX, g_stackguard1(DI)
MOVQ BX, (g_stack+stack_lo)(DI)
MOVQ SP, (g_stack+stack_hi)(DI)Step 3: Bind m0 to the main thread (TLS setup) – The runtime sets thread‑local storage (TLS) so that fs points to m0.tls[1] . It stores the address of g0 in TLS, establishing the g → m relationship.
LEAQ runtime·m0+m_tls(SB), DI // DI = &m0.tls
CALL runtime·settls(SB) // set TLS base
MOVQ $0x123, g(BX) // verify TLS works
...Step 4: Process command‑line arguments – The runtime calls runtime·args to copy argc and argv into the package‑level variables argc and argv , which later become os.Args .
func args(c int32, v **byte) {
argc = c
argv = v
sysargs(c, v)
}Step 5: OS initialization – runtime·osinit gathers CPU count, page size, and other OS‑specific settings.
func osinit() {
ncpu = getproccount()
physHugePageSize = getHugePageSize()
osArchInit()
}Step 6: Scheduler initialization (schedinit) – Locks are set up, the maximum number of M threads is limited to 10 000, the stack and memory allocators are initialized, mcommoninit prepares m0 , and procresize creates the global allp slice of P structures based on CPU count or GOMAXPROCS .
During procresize , the first P ( allp[0] ) is bound to m0 and marked _Prunning ; the remaining Ps are placed on the idle list.
Step 7: Create the first goroutine – newproc is invoked with the function pointer runtime·main . It allocates a new G, sets its sched.pc to runtime·main , and enqueues it on allp[0] 's run queue.
func newproc(fn *funcval) {
gp := getg()
pc := getcallerpc()
systemstack(func() {
newg := newproc1(fn, gp, pc)
pp := getg().m.p.ptr()
runqput(pp, newg, true)
if mainStarted { wakep() }
})
}Step 8: Start M0 and begin GMP scheduling – The stub finally calls runtime·mstart , which never returns. mstart sets up g0 's scheduler fields and invokes schedule , which picks the newly created main G, switches to its stack via gogo , and jumps to runtime.main .
runtime.main performs the final bootstrap: it sets maxstacksize , starts the sysmon monitor goroutine, runs package initializers (including the user’s main package), enables the garbage collector, and finally calls exit(0) after the user’s main returns.
The article concludes with a summary of the entire startup flow, emphasizing how the Go runtime prepares the environment, creates the first goroutine, and launches the GMP scheduler that drives program execution.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.