阅读:        作者:并排逗比北边跑

《深入理解计算机系统》3.7过程

所谓过程在C语言中就是函数的意思.
本章将介绍,函数调用过程的细节.

栈帧

IA32程序用栈来支持程序的运行,栈用来存放调用时候暂存的数据.
它可以:

  • 传递函数的参数
  • 存储返回数据
  • 保存某些寄存器的数据,以便后面恢复

每一段函数都会在栈中构建一块空间,名为栈帧.之所以叫栈帧是因为这块空间用栈指针帧指针界定.

栈指针:%esp,s代表stack,它指向栈帧的顶部,该指针是可移动的.
帧指针:%ebp,b我猜应该是base,它指向栈帧的底部,该指针是不可移动的,常用它的地址加上偏移量来获取保存在栈中的数据,所以它又叫基址指针

当在一个函数中调用另一个函数时候,一般会先让%ebp指向%esp的位置,然后%esp自己往后跑,最终构建出一个栈帧空间.

为了描述栈帧,用一小段简单的程序举例

  1. int target(int b){
  2. b=b+2;
  3. return b;
  4. }
  5. void claller(){
  6. int a=2;
  7. int c=target(a);
  8. }

汇编:
我用的是windows下的GCC

  1. .file "1.c"
  2. .text
  3. .align 2
  4. .globl target
  5. .def target; .scl 2; .type 32; .endef
  6. target:
  7. pushl %ebp
  8. movl %esp, %ebp
  9. movl 8(%ebp), %eax
  10. addl $2, %eax
  11. popl %ebp
  12. ret
  13. .align 2
  14. .globl caller
  15. .def caller; .scl 2; .type 32; .endef
  16. caller:
  17. pushl %ebp
  18. movl %esp, %ebp
  19. subl $4, %esp
  20. movl $2, (%esp)
  21. call target
  22. addl $3, %eax
  23. leave
  24. ret

解释汇编之前先介绍几个指令

call

  • call Label 调用指定函数
  • call *Operand 间接调用指定函数,Operand可以是一个寄存器
  • call的作用是把返回地址入栈(就是call指令下面一条指令,也就是程序计数器的值)并跳转到目标代码位置

补充

  1. call next
  2. next:
  3. pop %eax

该程序段是唯一能把程序计数器中的值保存到%exa的方式

ret

  • 从栈中弹出数据(其实就是返回地址),并跳转到这个地址所指位置

补充
在函数开头,栈指针会自动向下移动一段距离来开辟栈帧空间,这个移动的距离是根据这段函数用到的数据得出来.另外有时候这个空间并不是全部都用掉,因为GCC坚持一个x86编程指导方针,也就是一个函数使用的栈空间必须是16字节的整数倍,这种方式是为了保证数据的严格对齐,所以有时候编译器会分配这种永远不会用的空间.

递归


太复杂了你们自己看着玩

寄存器的使用惯例

在上面的例子可以看出,但调用一个函数时候,调用者(caller)不会覆盖被调用者(target)稍后要使用的寄存器的值.
比如在target开始的时候,它先把%ebp的值保存到栈中,要返回caller的时候在取出来用.这里是被调用者来负责寄存器数据不被覆盖.
IA32采用了统一的寄存器使用惯例来指示那些寄存器的数据由谁来保存.
内容是:

  • 寄存器%eax,%edx,%ecx被划分为调用者保存的寄存器

    • 在调用某段函数前先把寄存器的值保存到调用者的栈帧中,在被调用者的程序内就随意使用寄存器,等返回到调用者的栈帧时,在吧之前保存的数据放回寄存器
  • 寄存器%ebx,%esi,%edi划分为被调用者保存的寄存器

    • 在被调用者的程序段中,如果某个地方要用那3个寄存器之一,先把寄存器中的数据保存到被调用者栈帧中,并在调用完成返回前把栈帧中的数据那回到寄存器中.就像上面那个例子


来自为知笔记(Wiz)


附件列表

     

    Tags: 过程   计算机系统   计算机   计算   系统