实验环境

  • Ubuntu 20.10 x86_64 5.4.0-48
  • Go 1.17.12

进程虚拟地址空间


栈帧分配是在运行时动态申请和释放的, 如图所示,是进城虚拟地址空间组成的一部分,Linux 进程默认栈空间大小8MB, 那么栈空间如何申请和释放的呢?请往下看…

ubuntu@localhost:~$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 14635
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 14635
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

栈空间申请和释放

栈空间的申请和释放主要和两个寄存器有关:

  • BP (Base Pointer) 栈基
  • SP (Stack Pointer) 栈指针

栈申请

SUBQ    $48, SP         //SUBQ 减法  栈申请48字节,因为栈是从高地址--> 低地址
MOVQ    BP, 40(SP)      //存储 BP值
LEAQ    40(SP), BP      //更新 BP值

栈释放

MOVQ    40(SP), BP    //还原 BP值
ADDQ    $48, SP       //ADDQ 加法  栈释放48字节,因为栈是从高地址--> 低地址
RET                   //返回执行下一条指令

函数栈空间

  • caller 函数调用方 (A)
  • callee 函数被调用方 (B)

GoLang 语言 caller 负责 callee 的参数和返回值空间的申请和释放

示例验证

package main
//go:noinline
func sum(a, b int) int {
	sum := 0
	sum = a + b
	return sum
}
//go:noinline
func main() {
	a := 3
	b := 5
	c := sum(a, b)
	a = c + b
}
go tool compile -N -l -S cmd.go | less   //查看汇编代码
"".sum STEXT nosplit size=70 args=0x10 locals=0x18 funcid=0x0
.....
MOVQ    AX, "".a+32(SP) 访问到"".main的栈空间, 即 "".a+32(SP) =="".main+0(SP)
MOVQ    BX, "".b+40(SP) 访问到"".main的栈空间, 即 "".b+40(SP) =="".main+8(SP)
...
"".main STEXT size=88 args=0x0 locals=0x30 funcid=0x0
...
MOVQ    AX, "".c+16(SP) 访问到
...

验证:GoLang 语言 caller 负责 callee 的参数和返回值空间的申请和释放

遗留问题

"".sum STEXT nosplit size=70 args=0x10 locals=0x18 funcid=0x0
  0x0000 00000 (cmd.go:4) TEXT    "".sum(SB), NOSPLIT|ABIInternal, $24-16
  ...

$24-16: $24 表示栈大小,16网友描述说是参数+返回值大小,那么应该是18,但实际是16, 感觉只是参数的大小。如果你知道原因,请在评论区留言,感谢。