Goroutine的创建

src/runtime/proc.go

  1. type funcval struct {
  2. fn uintptr
  3. // variable-size, fn-specific data here
  4. }
  5. //go:nosplit
  6. // siz:参数长度 fn==>fn.fn才是申明的go func(){}
  7. func newproc(siz int32, fn *funcval) {
  8. argp := add(unsafe.Pointer(&fn), sys.PtrSize) // 将参数紧随fn.fn的地址
  9. gp := getg() // 当前g
  10. pc := getcallerpc() // 调用返回后的地址
  11. systemstack(func() {
  12. newproc1(fn, (*uint8)(argp), siz, gp, pc)
  13. })
  14. }
  15. // fn.fn == go func(){}
  16. // argp 参数的地址
  17. // narg 参数的长度
  18. // callergp 调用者g
  19. // callerpc 调用返回后的地址
  20. func newproc1(fn *funcval, argp *uint8, narg int32, callergp *g, callerpc uintptr) {
  21. _g_ := getg()
  22. if fn == nil {
  23. _g_.m.throwing = -1 // do not dump full stacks
  24. throw("go of nil func value")
  25. }
  26. _g_.m.locks++ // disable preemption because it can be holding p in a local var
  27. siz := narg
  28. siz = (siz + 7) &^ 7 // 进行8位对齐
  29. // 参数长度不能超过2008字节(2048-4*8-8)
  30. if siz >= _StackMin-4*sys.RegSize-sys.RegSize {
  31. throw("newproc: function arguments too large for new goroutine")
  32. }
  33. // 获取到当前g的p
  34. _p_ := _g_.m.p.ptr()
  35. // 从p的缓存池中去拿一个g
  36. newg := gfget(_p_)
  37. if newg == nil {
  38. // 缓冲池中没有,则需要新创建一个,栈大小为2k
  39. newg = malg(_StackMin)
  40. casgstatus(newg, _Gidle, _Gdead)
  41. allgadd(newg) // 新创建的g置位_Gdead状态,防止GC扫描到
  42. }
  43. if newg.stack.hi == 0 {
  44. throw("newproc1: newg missing stack")
  45. }
  46. if readgstatus(newg) != _Gdead {
  47. throw("newproc1: new g is not Gdead")
  48. }
  49. totalSize := 4*sys.RegSize + uintptr(siz) + sys.MinFrameSize // extra space in case of reads slightly beyond frame
  50. totalSize += -totalSize & (sys.SpAlign - 1) // align to spAlign
  51. sp := newg.stack.hi - totalSize
  52. spArg := sp
  53. if usesLR {
  54. // caller's LR
  55. *(*uintptr)(unsafe.Pointer(sp)) = 0
  56. prepGoExitFrame(sp)
  57. spArg += sys.MinFrameSize
  58. }
  59. if narg > 0 {
  60. memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
  61. // This is a stack-to-stack copy. If write barriers
  62. // are enabled and the source stack is grey (the
  63. // destination is always black), then perform a
  64. // barrier copy. We do this *after* the memmove
  65. // because the destination stack may have garbage on
  66. // it.
  67. if writeBarrier.needed && !_g_.m.curg.gcscandone {
  68. f := findfunc(fn.fn)
  69. stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
  70. if stkmap.nbit > 0 {
  71. // We're in the prologue, so it's always stack map index 0.
  72. bv := stackmapdata(stkmap, 0)
  73. bulkBarrierBitmap(spArg, spArg, uintptr(bv.n)*sys.PtrSize, 0, bv.bytedata)
  74. }
  75. }
  76. }
  77. memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
  78. newg.sched.sp = sp // 保存栈顶
  79. newg.stktopsp = sp
  80. newg.sched.pc = funcPC(goexit) + sys.PCQuantum // 这里需要结合gostartcallfn去了解
  81. newg.sched.g = guintptr(unsafe.Pointer(newg))
  82. gostartcallfn(&newg.sched, fn)
  83. newg.gopc = callerpc
  84. newg.ancestors = saveAncestors(callergp)
  85. newg.startpc = fn.fn
  86. if _g_.m.curg != nil {
  87. newg.labels = _g_.m.curg.labels
  88. }
  89. if isSystemGoroutine(newg, false) {
  90. atomic.Xadd(&sched.ngsys, +1)
  91. }
  92. newg.gcscanvalid = false
  93. casgstatus(newg, _Gdead, _Grunnable)
  94. if _p_.goidcache == _p_.goidcacheend {
  95. // Sched.goidgen is the last allocated id,
  96. // this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch].
  97. // At startup sched.goidgen=0, so main goroutine receives goid=1.
  98. _p_.goidcache = atomic.Xadd64(&sched.goidgen, _GoidCacheBatch)
  99. _p_.goidcache -= _GoidCacheBatch - 1
  100. _p_.goidcacheend = _p_.goidcache + _GoidCacheBatch
  101. }
  102. newg.goid = int64(_p_.goidcache)
  103. _p_.goidcache++
  104. runqput(_p_, newg, true)
  105. // 有空闲的p存在,并且处于自旋状态的m数量为0,而且在主函数已经运行的情况下
  106. // 尝试去唤醒某个m
  107. if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 && mainStarted {
  108. wakep()
  109. }
  110. _g_.m.locks--
  111. if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack
  112. _g_.stackguard0 = stackPreempt
  113. }
  114. }
  115. func gostartcallfn(gobuf *gobuf, fv *funcval) {
  116. var fn unsafe.Pointer
  117. if fv != nil {
  118. fn = unsafe.Pointer(fv.fn)// fv.fn: gorotine的入口地址
  119. } else {
  120. fn = unsafe.Pointer(funcPC(nilfunc))
  121. }
  122. gostartcall(gobuf, fn, unsafe.Pointer(fv))
  123. }
  124. func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
  125. sp := buf.sp
  126. if sys.RegSize > sys.PtrSize {
  127. sp -= sys.PtrSize
  128. *(*uintptr)(unsafe.Pointer(sp)) = 0
  129. }
  130. sp -= sys.PtrSize // 栈顶往下移动8位,为返回地址预留空间
  131. // 将fn伪装成是函数goexit函数调用的,函数fn执行完成返回到goexit继续执行,完成清理操作
  132. *(*uintptr)(unsafe.Pointer(sp)) = buf.pc
  133. buf.sp = sp
  134. //这里才真正让newg的ip寄存器指向fn函数,注意,这里只是在设置newg的一些信息,newg还未执行,
  135. //等到newg被调度起来运行时,调度器会把buf.pc放入cpu的IP寄存器,
  136. //从而使newg得以在cpu上真正的运行起来
  137. buf.pc = uintptr(fn)
  138. buf.ctxt = ctxt
  139. }

参考

https://mp.weixin.qq.com/s/8OffE6xDj4fD1q-aJw43ig
http://xiaorui.cc/archives/6483