前面简单介绍了一些常见的汇编指令,接下来我们谈一下在程序执行中一个非常重要的概念:过程

过程是软件工程中一种重要的抽象。它使得我们可以复用之前的代码,隐藏具体的实现。不同的编程语言或范式中它有不同的名字:函数,方法,等等。

假设P调用过程Q,Q执行后返回P。这些动作包括下面一个或多个机制:

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

C语言的实现

C语言实现“调用”机制的一个关键特性便是在于使用栈所提供的先进后出的内存管理原则。 在P调用Q时,栈会向上增长,为Q分配内存。而当Q结束后,栈指针减小重新回到P中。

控制转移

将控制从函数P中转移到Q中只需要把程序计数器设置为Q的起始地址就行了。不过它还需要把返回地址 先压入栈中,指明Q返回时要从哪里继续。在x86_64机器中,这些操作由call完成。

数据传送

当调用一个过程时,除了要把控制权转移给它外,还可能把数据作为参数传递给它。如果参数的个数 小于或等于6个,可以通过寄存器实现。而如果参数的个数大于6个,剩余的部分就要通过栈来传递。

内存分配

寄存器是在过程中唯一被共享的资源。我们必须保证当某一时刻调用另一个过程时,被调用者不会覆盖调用者稍后会使用的的寄存器。在x86_64中,有以下规则:

  • 被调用者保存寄存器

当过程P调用过程Q时,Q必须保存这些值。Q可以完全不去改变这些值,也可以先将旧值压入栈中,使用他们,最后弹出栈。这样就可以保证寄存器中的值不会遭到破坏。

寄存器%rbx %rbp 和%12 ~ %r15都属于这个。

  • 调用者保存寄存器

这意味着所有函数都可以随意更改他们的值,调用者必须自行保存好这个数据。

除%rsp外的所有非被调用者保存寄存器都属于这个。