《深入理解计算机系统》3.7过程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《深入理解计算机系统》3.7过程相关的知识,希望对你有一定的参考价值。
所谓过程在C语言中就是函数的意思.
本章将介绍,函数调用过程的细节.
栈帧
IA32程序用栈来支持程序的运行,栈用来存放调用时候暂存的数据.
它可以:
- 传递函数的参数
- 存储返回数据
- 保存某些寄存器的数据,以便后面恢复
每一段函数都会在栈中构建一块空间,名为栈帧.之所以叫栈帧是因为这块空间用栈指针
和帧指针
界定.
栈指针:%esp
,s代表stack,它指向栈帧的顶部,该指针是可移动的.
帧指针:%ebp
,b我猜应该是base,它指向栈帧的底部,该指针是不可移动的,常用它的地址加上偏移量来获取保存在栈中的数据,所以它又叫基址指针
当在一个函数中调用另一个函数时候,一般会先让%ebp指向%esp的位置,然后%esp自己往后跑,最终构建出一个栈帧空间.
为了描述栈帧,用一小段简单的程序举例
int target(int b){
b=b+2;
return b;
}
void claller(){
int a=2;
int c=target(a);
}
汇编:
我用的是windows下的GCC
.file "1.c"
.text
.align 2
.globl target
.def target; .scl 2; .type 32; .endef
target:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $2, %eax
popl %ebp
ret
.align 2
.globl caller
.def caller; .scl 2; .type 32; .endef
caller:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $2, (%esp)
call target
addl $3, %eax
leave
ret
解释汇编之前先介绍几个指令
call
- call Label 调用指定函数
- call *Operand 间接调用指定函数,Operand可以是一个寄存器
- call的作用是把返回地址入栈(就是call指令下面一条指令,也就是程序计数器的值)并跳转到目标代码位置
补充
call next
next:
pop %eax
该程序段是唯一能把程序计数器中的值保存到%exa的方式
ret
- 从栈中弹出数据(其实就是返回地址),并跳转到这个地址所指位置
补充
在函数开头,栈指针会自动向下移动一段距离来开辟栈帧空间,这个移动的距离是根据这段函数用到的数据得出来.另外有时候这个空间并不是全部都用掉,因为GCC坚持一个x86编程指导方针,也就是一个函数使用的栈空间必须是16字节的整数倍,这种方式是为了保证数据的严格对齐,所以有时候编译器会分配这种永远不会用的空间.
递归
寄存器的使用惯例
在上面的例子可以看出,但调用一个函数时候,调用者(caller)不会覆盖被调用者(target)稍后要使用的寄存器的值.
比如在target开始的时候,它先把%ebp的值保存到栈中,要返回caller的时候在取出来用.这里是被调用者来负责寄存器数据不被覆盖.
IA32采用了统一的寄存器使用惯例来指示那些寄存器的数据由谁来保存.
内容是:
寄存器
%eax
,%edx
,%ecx
被划分为调用者保存的寄存器- 在调用某段函数前先把寄存器的值保存到调用者的栈帧中,在被调用者的程序内就随意使用寄存器,等返回到调用者的栈帧时,在吧之前保存的数据放回寄存器
寄存器
%ebx
,%esi
,%edi
划分为被调用者保存的寄存器- 在被调用者的程序段中,如果某个地方要用那3个寄存器之一,先把寄存器中的数据保存到被调用者栈帧中,并在调用完成返回前把栈帧中的数据那回到寄存器中.就像上面那个例子
附件列表
以上是关于《深入理解计算机系统》3.7过程的主要内容,如果未能解决你的问题,请参考以下文章