1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| // func mcall(fn func(*g)) // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) // to keep running g. // // 根据注释,mcall 切换到 g0 栈调用 fn(g), fn 不能返回 TEXT runtime·mcall(SB), NOSPLIT, $0-8 // 栈帧大小为0,这种情况0(SP)保存的为返回地址 // 关于go的函数调用栈的布局,可以参考 http://mcll.top/2019/04/29/go函数栈布局 MOVQ fn+0(FP), DI // 将mcall的参数保存到DI寄存器,实际上是一个funcval指针 // 关于funcval的介绍,可以参考http://mcll.top/2019/03/06/go中的猴子补丁
get_tls(CX) // 将TLS保存到CX寄存器,TLS是一个伪寄存器,get_tls是一个宏,一直没找到其定义😓 MOVQ g(CX), AX // 保存当前g到AX寄存器中 MOVQ 0(SP), BX // 0(SP)保存返回地址,也就是mcall调用者的PC MOVQ BX, (g_sched+gobuf_pc)(AX) // 保存到g.sched.pc字段中 LEAQ fn+0(FP), BX // fn+0(FP)也就是调用者的SP MOVQ BX, (g_sched+gobuf_sp)(AX) // 保存到g.sched.sp字段中 MOVQ AX, (g_sched+gobuf_g)(AX) // 保存当前g到g.sched.g中 MOVQ BP, (g_sched+gobuf_bp)(AX) // 因为栈帧为0,BP就是调用者的BP,保存到g.sched.bp
// switch to m->g0 & its stack, call fn MOVQ g(CX), BX // 将原来的g保存到BX MOVQ g_m(BX), BX // 获取g.m,还是保存到BX MOVQ m_g0(BX), SI // 保存g0到SI CMPQ SI, AX // AX寄存器上面已经设置为原来的g了,禁止在g0栈上调用mcall,这里要判断一下 JNE 3(PC) // 如果原来的g不是g0,跳转到pc+3的位置执行,也就是从这里往下第三条指令 MOVQ $runtime·badmcall(SB), AX // 如果在g0上调用mcall,直接panic JMP AX // 跳转到 badmcall 方法,最终会panic MOVQ SI, g(CX) // 将g0设置到TLS中 MOVQ (g_sched+gobuf_sp)(SI), SP // 恢复g0的SP寄存器 PUSHQ AX // AX保存原来的g,入栈 MOVQ DI, DX // 上面说过,DI保存了要调用函数的funcval值,将其保存的DX寄存器,DX寄存器与闭包实现有关 MOVQ 0(DI), DI // 将要调用函数的入口地址保存到DI寄存器 CALL DI // 调用该函数,参数就是刚刚入栈的AX中的值,也就是原来的g,该函数禁止返回 POPQ AX // 出栈 MOVQ $runtime·badmcall2(SB), AX // 如果调用函数返回了,panic JMP AX RET
|