Goroutine的创建
src/runtime/proc.go
type funcval struct {fn uintptr// variable-size, fn-specific data here}//go:nosplit// siz:参数长度 fn==>fn.fn才是申明的go func(){}func newproc(siz int32, fn *funcval) {argp := add(unsafe.Pointer(&fn), sys.PtrSize) // 将参数紧随fn.fn的地址gp := getg() // 当前gpc := getcallerpc() // 调用返回后的地址systemstack(func() {newproc1(fn, (*uint8)(argp), siz, gp, pc)})}// fn.fn == go func(){}// argp 参数的地址// narg 参数的长度// callergp 调用者g// callerpc 调用返回后的地址func newproc1(fn *funcval, argp *uint8, narg int32, callergp *g, callerpc uintptr) {_g_ := getg()if fn == nil {_g_.m.throwing = -1 // do not dump full stacksthrow("go of nil func value")}_g_.m.locks++ // disable preemption because it can be holding p in a local varsiz := nargsiz = (siz + 7) &^ 7 // 进行8位对齐// 参数长度不能超过2008字节(2048-4*8-8)if siz >= _StackMin-4*sys.RegSize-sys.RegSize {throw("newproc: function arguments too large for new goroutine")}// 获取到当前g的p_p_ := _g_.m.p.ptr()// 从p的缓存池中去拿一个gnewg := gfget(_p_)if newg == nil {// 缓冲池中没有,则需要新创建一个,栈大小为2knewg = malg(_StackMin)casgstatus(newg, _Gidle, _Gdead)allgadd(newg) // 新创建的g置位_Gdead状态,防止GC扫描到}if newg.stack.hi == 0 {throw("newproc1: newg missing stack")}if readgstatus(newg) != _Gdead {throw("newproc1: new g is not Gdead")}totalSize := 4*sys.RegSize + uintptr(siz) + sys.MinFrameSize // extra space in case of reads slightly beyond frametotalSize += -totalSize & (sys.SpAlign - 1) // align to spAlignsp := newg.stack.hi - totalSizespArg := spif usesLR {// caller's LR*(*uintptr)(unsafe.Pointer(sp)) = 0prepGoExitFrame(sp)spArg += sys.MinFrameSize}if narg > 0 {memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))// This is a stack-to-stack copy. If write barriers// are enabled and the source stack is grey (the// destination is always black), then perform a// barrier copy. We do this *after* the memmove// because the destination stack may have garbage on// it.if writeBarrier.needed && !_g_.m.curg.gcscandone {f := findfunc(fn.fn)stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))if stkmap.nbit > 0 {// We're in the prologue, so it's always stack map index 0.bv := stackmapdata(stkmap, 0)bulkBarrierBitmap(spArg, spArg, uintptr(bv.n)*sys.PtrSize, 0, bv.bytedata)}}}memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))newg.sched.sp = sp // 保存栈顶newg.stktopsp = spnewg.sched.pc = funcPC(goexit) + sys.PCQuantum // 这里需要结合gostartcallfn去了解newg.sched.g = guintptr(unsafe.Pointer(newg))gostartcallfn(&newg.sched, fn)newg.gopc = callerpcnewg.ancestors = saveAncestors(callergp)newg.startpc = fn.fnif _g_.m.curg != nil {newg.labels = _g_.m.curg.labels}if isSystemGoroutine(newg, false) {atomic.Xadd(&sched.ngsys, +1)}newg.gcscanvalid = falsecasgstatus(newg, _Gdead, _Grunnable)if _p_.goidcache == _p_.goidcacheend {// Sched.goidgen is the last allocated id,// this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch].// At startup sched.goidgen=0, so main goroutine receives goid=1._p_.goidcache = atomic.Xadd64(&sched.goidgen, _GoidCacheBatch)_p_.goidcache -= _GoidCacheBatch - 1_p_.goidcacheend = _p_.goidcache + _GoidCacheBatch}newg.goid = int64(_p_.goidcache)_p_.goidcache++runqput(_p_, newg, true)// 有空闲的p存在,并且处于自旋状态的m数量为0,而且在主函数已经运行的情况下// 尝试去唤醒某个mif atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 && mainStarted {wakep()}_g_.m.locks--if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack_g_.stackguard0 = stackPreempt}}func gostartcallfn(gobuf *gobuf, fv *funcval) {var fn unsafe.Pointerif fv != nil {fn = unsafe.Pointer(fv.fn)// fv.fn: gorotine的入口地址} else {fn = unsafe.Pointer(funcPC(nilfunc))}gostartcall(gobuf, fn, unsafe.Pointer(fv))}func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {sp := buf.spif sys.RegSize > sys.PtrSize {sp -= sys.PtrSize*(*uintptr)(unsafe.Pointer(sp)) = 0}sp -= sys.PtrSize // 栈顶往下移动8位,为返回地址预留空间// 将fn伪装成是函数goexit函数调用的,函数fn执行完成返回到goexit继续执行,完成清理操作*(*uintptr)(unsafe.Pointer(sp)) = buf.pcbuf.sp = sp//这里才真正让newg的ip寄存器指向fn函数,注意,这里只是在设置newg的一些信息,newg还未执行,//等到newg被调度起来运行时,调度器会把buf.pc放入cpu的IP寄存器,//从而使newg得以在cpu上真正的运行起来buf.pc = uintptr(fn)buf.ctxt = ctxt}
参考
https://mp.weixin.qq.com/s/8OffE6xDj4fD1q-aJw43ig
http://xiaorui.cc/archives/6483
