CSAPP-程序的机器级表示(3)

122 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

这篇文章主要介绍过程调用具体在 CPU 和内存中是怎么实现的。理解之后,对于递归会有更加清晰的认识。

过程调用

  1. 传递控制:调用执行过程代码(call),以及返回调用函数的位置(ret)
  2. 传递数据:包括过程需要的参数以及过程的返回值
  3. 内存管理:如何在过程执行的时候分配内存,以及在返回之后释放内存

栈帧

当函数执行所需要的存储空间超出寄存器能够存放的大小时

就会借助栈上的存储空间,我们把这部分存储空间称为函数的栈帧

image.png


运行时栈

所谓的栈,实际上一块内存区域,可以看做是一块连续的大数组

下图中箭头所指的就是寄存器 %rsp 的值,这个寄存器是栈指针 ,用来记录栈顶的位置。

  • 往栈内添加数据,其地址是递减的
  • 栈底存储的是最先放入栈中的数据,满足先进后出的原则。
  • 因此栈顶的地址是最小的。

弹栈压栈

push操作(压栈):对应两个操作 -> %rsp地址-8,将操作数的值写入到栈中对应的位置

pop操作(弹栈):也对应两个操作 -> %rsp地址+8,将栈中位置的数据返回(对象是一个寄存器)

对于pop操作,其实只是移动了栈指针的位置,并不意味着内存上数据的"消除",这是那块数据不再是栈的一部分了


call指令与ret指令

指令call要做两件事:

  1. 将函数的第一条指令的地址写入到程序指令寄存器rip中,以此实现函数调用。
  2. 同时还要将返回地址压入栈中。

这个返回地址就是函数调用执行完毕后,下一条指令的地址。

  1. 当函数执行完毕,指令ret从栈中将返回地址弹出,写入到程序指令寄存器rip中。
  2. 函数返回,继续执行main函数中相关的操作。以上整个过程就是函数调用与返回所涉及的操作。