前一节schedinit完成了调度器的初始化,接下来会通过newproc创建一个goroutine来执行runtime.main函数
创建main goroutine
asm_amd64.s:214
// create a new goroutine to start program
MOVQ $runtime·mainPC(SB), AX // entry 这里mainPC是runtime.main
PUSHQ AX // 作为newproc的第二个参数
PUSHQ $0 // arg size // 作为newproc的第一个参数
CALL runtime·newproc(SB)
POPQ AX
POPQ AX
proc.go:4250
func newproc(siz int32, fn *funcval) {
argp := add(unsafe.Pointer(&fn), sys.PtrSize) // 获得fn的参数的地址
gp := getg() //gp=g0
pc := getcallerpc() // 返回caller‘s PC,就是下一条指令的地址,为POPQ AX
systemstack(func() { // 切换到g0的栈空间,但是此时本来就是执行在g0上
newg := newproc1(fn, argp, siz, gp, pc)
_p_ := getg().m.p.ptr()
runqput(_p_, newg, true)
if mainStarted {
wakep()
}
})
}
proc.go:4275
newproc1的参数如下:
- fn其实就是runtime.main的地址
- argp为runtime.main的参数开始地址
- narg为runtime.main的参数个数
- calllergp此时为g0
- callerpc为
CALL runtime·newproc(SB)下一条指令POPQ AX的地址
// Create a new g in state _Grunnable, starting at fn, with narg bytes
// of arguments starting at argp. callerpc is the address of the go
// statement that created this. The caller is responsible for adding
// the new g to the scheduler.
//
// This must run on the system stack because it's the continuation of
// newproc, which cannot split the stack.
//
//go:systemstack
func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) *g {
if goexperiment.RegabiDefer && narg != 0 {
// TODO: When we commit to GOEXPERIMENT=regabidefer,
// rewrite the comments for newproc and newproc1.
// newproc will no longer have a funny stack layout or
// need to be nosplit.
throw("go with non-empty frame")
}
_g_ := getg() // _g_=g0
if fn == nil {
_g_.m.throwing = -1 // do not dump full stacks
throw("go of nil func value")
}
acquirem() // disable preemption because it can be holding p in a local var
siz := narg
siz = (siz + 7) &^ 7
// We could allocate a larger initial stack if necessary.
// Not worth it: this is almost always an error.
// 4*PtrSize: extra space added below
// PtrSize: caller's LR (arm) or return address (x86, in gostartcall).
if siz >= _StackMin-4*sys.PtrSize-sys.PtrSize {
throw("newproc: function arguments too large for new goroutine")
}
_p_ := _g_.m.p.ptr() // _p_=allp[0]
newg := gfget(_p_) // 初始化阶段,gf里面是空的,所以接下来要new一个
if newg == nil {
newg = malg(_StackMin) // 2K大小
casgstatus(newg, _Gidle, _Gdead)
allgadd(newg) // publishes with a g->status of Gdead so GC scanner doesn't look at uninitialized stack.
}
if newg.stack.hi == 0 {
throw("newproc1: newg missing stack")
}
if readgstatus(newg) != _Gdead {
throw("newproc1: new g is not Gdead")
}
totalSize := 4*sys.PtrSize + uintptr(siz) + sys.MinFrameSize // extra space in case of reads slightly beyond frame
totalSize += -totalSize & (sys.StackAlign - 1) // align to StackAlign
sp := newg.stack.hi - totalSize //totalSize=32
spArg := sp // 确定参数位置
if usesLR {
// caller's LR
*(*uintptr)(unsafe.Pointer(sp)) = 0
prepGoExitFrame(sp)
spArg += sys.MinFrameSize
}
if narg > 0 {
memmove(unsafe.Pointer(spArg), argp, uintptr(narg)) //将参数从g0栈拷贝到newg的栈
(...)
}
(...)
}
proc.go:4346
memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched)) //将newg.sched成员变量清空
newg.sched.sp = sp //sched用来保存newg的调度信息,依靠它来将goroutine切到cpu上
newg.stktopsp = sp
// newg.sched.pc代表newg被调度起来时执行第一行代码的位置,为goexit的第二条指令
newg.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
newg.sched.g = guintptr(unsafe.Pointer(newg))
gostartcallfn(&newg.sched, fn)
// adjust Gobuf as if it executed a call to fn
// and then stopped before the first instruction in fn.
func gostartcallfn(gobuf *gobuf, fv *funcval) {
var fn unsafe.Pointer
if fv != nil {
fn = unsafe.Pointer(fv.fn)
} else {
fn = unsafe.Pointer(funcPC(nilfunc))
}
gostartcall(gobuf, fn, unsafe.Pointer(fv))
}
// adjust Gobuf as if it executed a call to fn with context ctxt
// and then stopped before the first instruction in fn.
func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
sp := buf.sp
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = buf.pc
buf.sp = sp
buf.pc = uintptr(fn)
buf.ctxt = ctxt
}
proc.go:4254
systemstack(func() {
newg := newproc1(fn, argp, siz, gp, pc)
_p_ := getg().m.p.ptr()
runqput(_p_, newg, true) //将newg放入p的队列里
if mainStarted {
wakep()
}
})