07 Mechine level Programing III Procedures

Procedures(过程)

过程是软件中一种很重要的抽象。

  • 提供了一种封装代码的方式:用一组指定的参数和一个可选的返回值实现了某种功能。然后,可以在程序中不同的地方调用这个函数。
  • 不同编程语言中,过程的形式多样:函数(function)、方法(method)、子例程(subroutine)、处理函数(handler)等等,但是它们有一些共有的特性。

要提供对过程的机器级支持,必须要处理许多不同的属性
假设过程P 调用过程 Q,Q 执行后返回到 P,这些动作包括下面一个或多个机制:

  • 传递控制。进入过程Q 之前, 程序计数器 必须被设置为 Q的代码的起始地址,然后在返回的时候,把程序计数器设置为P中调用Q后面的地址
  • 传递数据。P 必须能够向 Q 提供一个或多个参数,Q 必须能够向 P 返回一个值。
  • 分配和释放内存。在开始时,Q 可能需要为局部变量分配空间,而在返回前,又必须释放这些存储空间。

7.1 运行时栈

  • 当 x86-64 过程需要的存储空间超过寄存器能够存放的大小时, 就会在栈上分配空间----->这个过程被称为 过程 Stack Frame
  • 当然如果需要存储的参数不多,那么直接在寄存器上分配
    在这里插入图片描述
    在这里插入图片描述

7.2 转移控制

将控制从函数 P 转移到函数 Q 只需要:把程序计数器(PC)设置为 Q 的代码的起始位置当稍后从 Q 返回的时候,处理器必须记录好它需要继续 P 的执行的代码位置
在x86-64 机器中,这个信息是用指令 call Q 调用过程 Q 来记录的。该指令会把地址 A 压入栈中,并将 PC 设置为 Q 的起始地址。压人的地址 A 被称为返回地址,是紧跟在 call 指令后面的那条指令的地址。对应的指令 ret 会从栈中弹出地址 A,并把 PC 设置为 A。

7.3数据传送

当调用一个过程时,除了要把控制传递给它并在过程返回时再传递回来之外,过程调用还可能包括把数据作为参数传递,而从过程返回还有可能包括返回一个值。X86-64 中,大部分过程间的数据传送是通过寄存器实现的。

7.4 栈上的局部存储

到目前为止我们看到的大多数过程示例都不需要超出寄存器大小的本地存储区域。不过有些时候,局部数据必须存放在内存中,常见的情况包括:

  • 寄存器不足够存放所有的本地数据。
  • 对一个局部变量使用地址运算符 ‘&’, 因此必须能够为它产生一个地址。
  • 某些局部变量是数组或者结构, 因此必须通过数组 / 结构的引用实现访问
    一般来说,过程通过减少栈指针在栈上分配空间, 分配的结果作为Stack Frame 的一部分

在这里插入图片描述
案例分析:

long call_proc(){
  long x1 = 1;  int x2 = 2;
  short x3 = 3; char x4 = 4;
  proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4);
  return (x1 + x2) * (x3 - x4);
}

在这里插入图片描述

  • 将四个指针的值,按照sizeof 的大小进行分配
    在这里插入图片描述

7.5寄存器中的局部存储空间

寄存器组是唯一被所有过程共享的资源。

虽然在给定时刻只有一个过程是活动的,我们仍然必须确保当一个过程(调用者)调用另一个过程(被调用者)时,被调用者不会覆盖调用者稍后会使用的寄存器值。为此,X86-64 采用了一组统一的寄存器使用惯例,所有的过程(包括程序库)都必须遵循

7.6递归过程

 前面已经描述的寄存器和栈的惯例使得 x86-64 过程能够**递归地调用它们自身。**每个过程调用在栈中都有它自己的私有空间因此多个未完成调用的局部变量不会相互影响。此外,**栈的原则很自然地就提供了适当的策略,当过程被调用时分配局部存储,当返回时释放存储。**
 栈分配和释放的规则很自然地就与函数调用-返回的顺序匹配。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_56898461/article/details/129683230
07
今日推荐