- Code Path:
src/runtime/proc.go
Start-Up Process of a Go program.
Take src/runtime/asm_arm.s as an example:
- Take the address of
g0andm0. - Set up
m.g0andg.m. - Create
istack. - Do runtime check.
- Save
argcandargv. - Call
runtime.osinit. - Call
runtime.schedinit. - Call
runtime.newprocfor the main function. - call
runtime.mstartto start the M.
runtime.osinit
Take src/runtime/os_linux.go as an example:
- Get the number of processors. This is done by making a syscall
SYS_sched_getaffinity. - Get the huge page size.
- Run osArchInit. <- Seems not used.
runtime.schedinit
- Initialize all the locks.
- Set up the
g.racectx. - Stop the world.
moduledataverify. Defined insrc/runtime/symtab.go, it will checkmoduledata’s binary info.stackinit. Defined insrc/runtime/stack.go, it will setup the global stack pool, all stacks will be allocated by it.mallocinit. Defined insrc/runtime/malloc.go, it will initialize the memory allocator used by Go.cpuinit. Set up theinternal/cpuinformation.alginit. Defined insrc/runtime/alg.go, set up the CPU instructions that will be used by some internal algorithms, depending oncpuinit.fastrandinit.- Initialize the
g0.mwithmcommoninit. modulesinit. Defined insrc/runtime/symtab.go, initialize the dynamic loaded modules.typelinksinit. Defined insrc/runtime/type.go, initialized the dynamic-loaded module type informations.itabsinit. Defined insrc/runtime/iface.go, updateitabTablewith the dynamic-loaded modules.stkobjinit. Defined insrc/runtime/stkframe.go, this will set upmethodValueCallFrameObjs, used by later GC module.- Save the signal mask as
initSigMask. - Parse
argv. Defined insrc/runtime/runtime1.go. - Parse environment variables.
parsedebugvars. Defined insrc/runtime/runtime1.go.gcinit.- Set up the number of processors, and trim it accordingly with
procresize:- Grow
allpas necessary. - Initialize all the
p’s ofallp. - Release old
p’s. - Track the idle and runnable
p, and return the runnablep’s.
- Grow
- There should be zero runnable
p, since this is only a bootstrap process. - Start the world.
runtime.mstart
mstart0 set up the stack info, then calls runtime.mstart1 to allocate the m:
- Check whether the current
gism’s schedulingg0. - Set up the
m.g0, mostly the same asnewproc. - Init the assembly part with
asminit, ISA-dependent. - Init the thread-level information with
minit, OS-dependent. - If the current
mism0, spin up an extram, and initialize signals. - Call
m.mstartfn. - If it’s not
m0, acquire them’s p. schedule().
Start a New Goroutine: runtime.newproc
In Go, you start a goroutine with the go keyword, this compiler will expand this keyword as runtime.newproc.
getgto fetch the currengginformation.getcallerpcto fetch the pc register of the caller. This function is expanded to assembly code by the compiler asssa.OpGetCallerPC.- Run the goroutine from
systemstack.systemstackis used to run the function with a goroutine stack. acquiremto get themof currentg.- Get a free
gfromp.- First check the
p.gFree, if empty, try to get from the globalsched.gFree. - Pop a free
gfromp.gFree. - Clear the
g’s old stack and allocate a new stack for it.
- First check the
- If there’s no free
gallocated before, allocate a new one.- Create a new
g. - Allocate the memory for system stack size + desired stack size.
- Change the
gstatus to_Gdead. - Add the new
gto the globalallgslist.
- Create a new
- Reserve some extra memory at the top of the stack, and shift
spregister accordingly. - Set up the
g.sched. It containssp,pc, and the newg’s address. - Set up the
g.sched, and get ready for the actual function call. - Save the caller’s pc into
g.gopc. - Save the ancestor info into
g.ancestors. - Track the profiling information for system and user-defined goroutines separately.
- Switch
gstatus to_Grunnable. - Track the stack at GC.
- Generate
g.goid. - Put the
gon the runnable queue. - Try to add more
pto executeg.
runtime.wakep
wakep is called at the last step of newproc, tring to run the _Grunnable g. This step will make sure that there’s always a spinning m ready to run the new g.
- If there’s already a spinning
p, return immediately. - Lock the current
m. - Try to get a spinning
p. - Start a new
mto hold thep.
Scheduling Cycle: runtime.schedule
Callers: mstart0, gopark, goschedguarded/gopreempt_m, preemptPark, goyield, goexit1, exitsyscall.
Let’s try to analyze these callers one by one.
runtime.mstart0
See above.
runtime.goschedImpl
It’s a common function to schedule a goroutine:
- Check
gstatus is_Grunning. - Change
gstatus to_Grunnable. - Detach
gfrom the currentm. - Put
gonto the global run queue. schedule().
runtime.gopark
See the last post.
runtime.gopreempt_m, runtime.goschedguarded
These are wrappers around goschedImpl.
runtime.preemptPark
This function is similar to goschedImpl, but switches the g status to _Gpreempted.
runtime.goyield
This function is similar to goschedImpl, but without the _Grunning check, because it’s called from the current goroutine to yield the current m.
runtime.goexit1
goexit1 will finish the current goroutine.
- Change the status to
_Gdead. - Release all
g’s resources. - Detach
gfrom the currentm. - Put
gon the free list. - If the
gis locked, kill the thread directly. schedule().
runtime.exitsyscall
exitsyscall will only be called by syscalls, after the syscall is finished, it will call this function to switch back to the goroutine.
- Try to acquire the old
por get one idlep. If acquired, no need to do anything, just return immediately.- If the old
pis still_Psyscall, switch to_Pidleand attachgto thisp. - Get a
pfrom the global idleplist, and attachgto thisp. - Switch the
gstatus to_Grunning.
- If the old
- Update
gstatus to_Grunnable. - Detach
gfrom them. - If
gcan be scheduled, try to get an idlep. - If there’s no idle
p:- Put
gon the global runnable queue. - If it’s locked,
startlockedm:- Stop the locked
mof the currentgif necessary. The current executinggon thatmwill be handed off to others. - Execute
g.
- Stop the locked
- Else:
- Stop the m and wait for an available
p. schedule().
- Stop the m and wait for an available
- Put
- Else:
- Notify
sysmonif necessary. - Attach
gto thepand run it. - Execute
g.
- Notify
The implementation of runtime.schedule
- If the
gis locked, stop thatmand rungon thatm. No scheduling in this part. - CGO is always handled in
g0, so it should never call theschedule(). - Find a runnable
gwithfindRunnable- Wait for GC stop-the-world.
- Get the timer information from the current
p. - Try to schedule a trace reader.
- Try to schedule a GC worker.
- Get one
gfrom the global runnable queue. Run it regularly but not on every cycle to ensure fairness. - Mark GC finblock’s as ready.
- If there’s a
cgo_yield, run it. - Get one
gfrom the local runnable queue. - Get one
gfrom the global runnable queue. - Poll the network.
- Steal the work from other
p’s. - There’s nothing to do:
- Run GC marking worker.
- Put back
pto the global idle list. - Poll the network.
- Stop the current
m. - Try it again until find a
g.
- Reset the spinning
m. - Wake up a
pfor internal goroutines (GC workers, etc). - If the
gis locked to anm, runstartlockedm. - Execute
g.
What’s Next
- Go’s memory allocator.
- Go’s stack allocator.