之前在看关于内联,宏和普通函数调用的时候想到了这个问题,感觉自己对函数调用底层的过程原理还是不太清楚,特意查了相关资料,这里分析记录下。
1、基础知识
1.1 基本概念
每个进程对应一个Call Stack(调用栈),而CallStack 又由许多Stack Frame(栈帧)组成,对应每个未完成的函数调用,由系统分配。
在多线程(任务)环境,CPU的栈指针指向的存储器区域就是当前使用的栈。切换线程的一个重要工作,就是将栈指针设为当前线程的栈栈顶地址。
栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。
函数调用时,需要准备好堆栈,而堆栈的作用就是:保存现场/上下文,传递参数。
具体来说:
1)保存寄存器的值,一般用的是push指令,将对应的某些寄存器的值,一个个放到堆栈中,把对应的值压入到堆栈里面,即所谓的压栈。
然后待被调用的子函数执行完毕的时候,再调用pop,把堆栈中的一个个的值,赋值给对应的那些你刚开始压栈时用到的寄存器,把对应的值从堆栈中弹出去,即所谓的出栈。
2)一种情况是,本身传递的参数就很少,就可以通过寄存器传送参数。但是如果参数太多,寄存器不够用,那么就得把多余的参数堆栈中了。
1.2 esp,ebp,eip
堆栈是一种简单的数据结构,是一种只允许在其一端进行插入或删除的线性表。
允许插入或删除操作的一端称为栈顶,另一端称为栈底,对堆栈的插入和删除操作被称入栈和出栈。
有一组CPU指令可以实现对进程的内存实现堆栈访问。其中,POP指令实现出栈操作,PUSH指令实现入栈操作。
CPU的ESP寄存器存放当前线程的栈顶指针,
EBP寄存器中保存当前线程的栈底指针。
CPU的EIP寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。
esp是堆栈指针
ebp是基址指针
esp始终指向栈顶,ebp是在堆栈中寻址用的