go version go16.2, maxOS Mojave
原理可以从https://juejin.cn/post/684490...,本文单纯解析源码
哪里调用
原函数
package main
func testMoreStack(a, b int) int {
return testMoreStack(1, b)
}
func main() {
}
编译
go tool compile -N -l -S ./main2.go > ./main2~~~~.s
打开生成文件main2.s
"".testMoreStack STEXT size=93 args=0x18 locals=0x28 funcid=0x0
0x0000 00000 (./main2.go:7) TEXT "".testMoreStack(SB), ABIInternal, $40-24
0x0000 00000 (./main2.go:7) MOVQ (TLS), CX // CX = *g
0x0009 00009 (./main2.go:7) CMPQ SP, 16(CX) // if SP > g.stackguard1
0x000d 00013 (./main2.go:7) PCDATA $0, $-2
0x000d 00013 (./main2.go:7) JLS 86 // true 跳到86
0x000f 00015 (./main2.go:7) PCDATA $0, $-1
0x000f 00015 (./main2.go:7) SUBQ $40, SP
0x0013 00019 (./main2.go:7) MOVQ BP, 32(SP)
0x0018 00024 (./main2.go:7) LEAQ 32(SP), BP
0x001d 00029 (./main2.go:7) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (./main2.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (./main2.go:7) MOVQ $0, "".~r2+64(SP)
0x0026 00038 (./main2.go:8) MOVQ "".b+56(SP), AX
0x002b 00043 (./main2.go:8) MOVQ $1, (SP)
0x0033 00051 (./main2.go:8) MOVQ AX, 8(SP)
0x0038 00056 (./main2.go:8) PCDATA $1, $0
0x0038 00056 (./main2.go:8) CALL "".testMoreStack(SB)
0x003d 00061 (./main2.go:8) MOVQ 16(SP), AX
0x0042 00066 (./main2.go:8) MOVQ AX, ""..autotmp_3+24(SP)
0x0047 00071 (./main2.go:8) MOVQ AX, "".~r2+64(SP)
0x004c 00076 (./main2.go:8) MOVQ 32(SP), BP
0x0051 00081 (./main2.go:8) ADDQ $40, SP
0x0055 00085 (./main2.go:8) RET
0x0056 00086 (./main2.go:8) NOP
0x0056 00086 (./main2.go:7) PCDATA $1, $-1
0x0056 00086 (./main2.go:7) PCDATA $0, $-2
0x0056 00086 (./main2.go:7) CALL runtime.morestack_noctxt(SB)
0x005b 00091 (./main2.go:7) PCDATA $0, $-1
0x005b 00091 (./main2.go:7) JMP 0 // 跳到0
0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 47 48 eH..%....H;a.vGH
0x0010 83 ec 28 48 89 6c 24 20 48 8d 6c 24 20 48 c7 44 ..(H.l$ H.l$ H.D
0x0020 24 40 00 00 00 00 48 8b 44 24 38 48 c7 04 24 01 $@....H.D$8H..$.
0x0030 00 00 00 48 89 44 24 08 e8 00 00 00 00 48 8b 44 ...H.D$......H.D
0x0040 24 10 48 89 44 24 18 48 89 44 24 40 48 8b 6c 24 $.H.D$.H.D$@H.l$
0x0050 20 48 83 c4 28 c3 e8 00 00 00 00 eb a3 H..(........
rel 5+4 t=17 TLS+0
rel 57+4 t=8 "".testMoreStack+0
rel 87+4 t=8 runtime.morestack_noctxt+0
本函数栈40字节
,调用栈24字节
,函数返回PC8字节
字节共72字节
,
栈位置:
位置 | 值 |
---|---|
64(SP) | 返回值 |
56(SP) | 参数b |
48(SP) | 参数a |
40(SP) | caller's PC |
32(SP) | base point |
24(SP) | 临时存调用testMoreStack的返回值 |
16(SP) | 调用testMoreStack的返回值 |
8(SP) | 调用testMoreStack的第二个参数 |
0(SP) | 调用testMoreStack的第一个参数 |
伪代码
label0:
if SP > g.stackguard1 {
CALL runtime.morestack_noctxt(SB)
jump label0
} else {
do something
}
开始执行
以asm_amd64.s为例
// morestack but not preserving ctxt.
TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
MOVL $0, DX // 一会传给g.sched.gobuf.ctxt
JMP runtime·morestack(SB)
// Called during function prolog when more stack is needed.
//
// The traceback routines see morestack on a g0 as being
// the top of a stack (for example, morestack calling newstack
// calling the scheduler calling newm calling gc), so we must
// record an argument size. For that purpose, it has no arguments.
TEXT runtime·morestack(SB),NOSPLIT,$0-0
// Cannot grow scheduler stack (m->g0).
get_tls(CX) // CX = g
MOVQ g(CX), BX // BX = g
MOVQ g_m(BX), BX // BX = g.m
MOVQ m_g0(BX), SI // SI = m.g0
CMPQ g(CX), SI // if g == g0
JNE 3(PC) // 不相等的话跳到 3(PC)
CALL runtime·badmorestackg0(SB)
CALL runtime·abort(SB)
// Cannot grow signal stack (m->gsignal).
MOVQ m_gsignal(BX), SI // SI = m.gsignal
CMPQ g(CX), SI // if g == gsignal
JNE 3(PC) // 不相等的话跳到 3(PC)
CALL runtime·badmorestackgsignal(SB)
CALL runtime·abort(SB)
// Called from f: f就是前面的testMoreStack
// Set m->morebuf to f's caller.
NOP SP // tell vet SP changed - stop checking offsets
MOVQ 8(SP), AX // f's caller's PC
MOVQ AX, (m_morebuf+gobuf_pc)(BX) // m.morebuf.gobuf.pc = 8(SP)
LEAQ 16(SP), AX // f's caller's SP
MOVQ AX, (m_morebuf+gobuf_sp)(BX) // m.morebuf.gobuf.pc = 8(SP)
get_tls(CX) // CX = g
MOVQ g(CX), SI // SI = g
MOVQ SI, (m_morebuf+gobuf_g)(BX) // m.morebuf.gobuf.g = g
// Set g->sched to context in f.
MOVQ 0(SP), AX // f's PC
MOVQ AX, (g_sched+gobuf_pc)(SI) // m.morebuf.gobuf.pc = AX
MOVQ SI, (g_sched+gobuf_g)(SI) // g.sched.gobuf.g = g
LEAQ 8(SP), AX // f's SP
MOVQ AX, (g_sched+gobuf_sp)(SI) // g.sched.gobuf.sp = AX
MOVQ BP, (g_sched+gobuf_bp)(SI) // g.sched.gobuf.bp = BP
MOVQ DX, (g_sched+gobuf_ctxt)(SI) // g.sched.gobuf.ctxt = DX = 0
// Call newstack on m->g0's stack.
MOVQ m_g0(BX), BX // BX = m.g0
MOVQ BX, g(CX) // g = BX = g0, 切换成g0来执行
MOVQ (g_sched+gobuf_sp)(BX), SP // g.sched.gobuf.sp = sp
CALL runtime·newstack(SB)
CALL runtime·abort(SB) // crash if newstack returns
RET
栈位置:
位置 | 值 |
---|---|
16(SP) | testMoreStack返回上一个函数的SP位置 |
8(SP) | testMoreStack返回上一个函数的指针 |
0(SP) | morestack返回testMoreStack的指针 |
newstack
// Called from runtime·morestack when more stack is needed.
// Allocate larger stack and relocate to new stack.
// Stack growth is multiplicative, for constant amortized cost.
//
// g->atomicstatus will be Grunning or Gscanrunning upon entry.
// If the scheduler is trying to stop this g, then it will set preemptStop.
//
// This must be nowritebarrierrec because it can be called as part of
// stack growth from other nowritebarrierrec functions, but the
// compiler doesn't check this.
//
//go:nowritebarrierrec
func newstack() {
thisg := getg() // 这个g是g0,前面在morestack把g切换成g0
gp := thisg.m.curg // 这个是调用morestack的g
// ..... 省略检测和log代码
morebuf := thisg.m.morebuf
thisg.m.morebuf.pc = 0
thisg.m.morebuf.lr = 0
thisg.m.morebuf.sp = 0
thisg.m.morebuf.g = 0
// 注意:如果另一个线程要抢占gp,stackguard0可能会发生变化。
// 只需读取一次,然后在下面和下面使用相同的值。
preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt
// Be conservative about where we preempt.
// We are interested in preempting user Go code, not runtime code.
// If we're holding locks, mallocing, or preemption is disabled, don't
// preempt.
// This check is very early in newstack so that even the status change
// from Grunning to Gwaiting and back doesn't happen in this case.
// That status change by itself can be viewed as a small preemption,
// because the GC might change Gwaiting to Gscanwaiting, and then
// this goroutine has to wait for the GC to finish before continuing.
// If the GC is in some way dependent on this goroutine (for example,
// it needs a lock held by the goroutine), that small preemption turns
// into a real deadlock.
if preempt {
if !canPreemptM(thisg.m) {
// Let the goroutine keep running for now.
// gp->preempt is set, so it will be preempted next time.
gp.stackguard0 = gp.stack.lo + _StackGuard
gogo(&gp.sched) // never return
}
}
if gp.stack.lo == 0 {
throw("missing stack in newstack")
}
sp := gp.sched.sp
if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 || sys.ArchFamily == sys.WASM {
// The call to morestack cost a word.
sp -= sys.PtrSize
}
if stackDebug >= 1 || sp < gp.stack.lo {
print("runtime: newstack sp=", hex(sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n",
"\tmorebuf={pc:", hex(morebuf.pc), " sp:", hex(morebuf.sp), " lr:", hex(morebuf.lr), "}\n",
"\tsched={pc:", hex(gp.sched.pc), " sp:", hex(gp.sched.sp), " lr:", hex(gp.sched.lr), " ctxt:", gp.sched.ctxt, "}\n")
}
if sp < gp.stack.lo {
print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->status=", hex(readgstatus(gp)), "\n ")
print("runtime: split stack overflow: ", hex(sp), " < ", hex(gp.stack.lo), "\n")
throw("runtime: split stack overflow")
}
if preempt {
if gp == thisg.m.g0 {
throw("runtime: preempt g0")
}
if thisg.m.p == 0 && thisg.m.locks == 0 {
throw("runtime: g is running but p is not")
}
if gp.preemptShrink {
// We're at a synchronous safe point now, so
// do the pending stack shrink.
gp.preemptShrink = false
shrinkstack(gp)
}
if gp.preemptStop {
preemptPark(gp) // never returns
}
// Act like goroutine called runtime.Gosched.
gopreempt_m(gp) // never return
}
// Allocate a bigger segment and move the stack.
oldsize := gp.stack.hi - gp.stack.lo
newsize := oldsize * 2
// Make sure we grow at least as much as needed to fit the new frame.
// (This is just an optimization - the caller of morestack will
// recheck the bounds on return.)
if f := findfunc(gp.sched.pc); f.valid() {
max := uintptr(funcMaxSPDelta(f))
for newsize-oldsize < max+_StackGuard {
newsize *= 2
}
}
if newsize > maxstacksize || newsize > maxstackceiling {
if maxstacksize < maxstackceiling {
print("runtime: goroutine stack exceeds ", maxstacksize, "-byte limit\n")
} else {
print("runtime: goroutine stack exceeds ", maxstackceiling, "-byte limit\n")
}
print("runtime: sp=", hex(sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n")
throw("stack overflow")
}
// The goroutine must be executing in order to call newstack,
// so it must be Grunning (or Gscanrunning).
casgstatus(gp, _Grunning, _Gcopystack)
// The concurrent GC will not scan the stack while we are doing the copy since
// the gp is in a Gcopystack status.
copystack(gp, newsize)
if stackDebug >= 1 {
print("stack grow done\n")
}
casgstatus(gp, _Gcopystack, _Grunning)
gogo(&gp.sched)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。