source:http://blog.csdn.net/qq_29403077/article/details/53205010

一、地址空间与物理内存
(1)地址空间与物理内存是两个完全不同的概念,真正的代码及数据都存在物理内存中。
物理储存器是指实际存在的具体储存器芯片,CPU在操纵物理储存器的时候都把他们当做内存来对待,把他们看成由若干个储存单元组成的逻辑储存器,这个逻辑储存器就是我们所说的地址空间。
地址空间大小与逻辑储存器大小不一定相等。
(2)进程的地址空间分布
进程的地址空间包括:栈区(heap)、共享区、堆区(stack)、未初始化静态全局区、已初始化静态全局区、静态只读区、代码段。如图:

二、栈帧的建立
首先要明白几个地方:
每一个函数都有自己的栈帧空间,并且独占自己的栈帧空间,
当前正在运行的函数的栈帧总是在栈顶。Win32系统提供两个特殊的寄存器用于标识位于系统栈顶端的栈帧。

  (1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶,即栈顶寄存器。
  
  (2)EBP:基址指针寄存器(extended base pointer),即栈底寄存器。 
  
  (3)栈帧状态值:保存前栈帧的顶部和底部(实际上只保存前栈帧的底部,前栈帧的顶部可以通过栈帧平衡计算得到),用于在本栈被弹出后恢复出上一个栈帧。
  
  (4)函数返回地址:保存当前函数调用前的“断点”信息,也就是函数调用前的指令位置,以便在函数返回时能够恢复到函数被调用前的代码区中继续执行指令。
  
  注:函数栈帧的大小并不固定,一般与其对应函数的局部变量多少有关。函数运行过程中,其栈帧大小也是在不停变化的。除了与栈相关的寄存器外,我们还需要记住另一个至关重要的寄存器。
  
  EIP:指令寄存器(extended instruction pointer),其内存放着一个指针,该指针永远指向下一条等待执行的指令地址。
  
   首先栈是自高地址向低地址即向下生长的。
  我们通过如下程序了解栈帧建立过程:

int fun(int a,int b)
{
int sum=0;
sum=a+b;
return sum;
}
int main()
{
fun(3,4);
printf("haha");
return 0;
}

这里main是调用者(caller);fun是被调用者(Callee)在函数调用前,main正在用ESP和EBP寄存器指示它自己的栈帧。

main把EAX,ECX和EDX压栈。这是一个可选的步骤,只在这三个寄存器内容需要保留的时候执行此步骤。
接着,main把传递给fun的参数一一进栈,最后的参数最先进栈,这里也就解释了函数在压栈过程中是从右往左,即先压4,再压3。

(1)这里首先main函数建立自己的栈帧结构;main()函数是由—__tCRTStartup()函数调用的,所以mainCRTStratup()函数调用__tmainCRTStra()函数的时候就会从栈上为__tmainCRTStra()分配类似图中这么一块空间,因为我们现在要调用main()函数了,所以当然要先把__tmainCRTStartup()函数的运行状态保存下来,这样main()函数才能返回的时候才能找得到!。

然后继续执行下一条语句: mov ebp,esp
即把esp的值赋给ebp,这样,ebp也就指向了现在esp的位置
然后sub esp 0C0h 这样就为main函数开辟了一段空间然后将ebx、esi、edi寄存器压栈就形成如图所示:

紧接着将局部变量及实参压栈,并执行call指令,main用call指令调用子函数:
call fun
当call指令执行的时候,EIP指令指针寄存器的内容被压入栈中。因为EIP寄存器是指向main中的下一条指令,所以现在返回地址就在栈顶了。在call指令执行完之后,下一个执行周期将从名为fun的标记处开始.


当函数fun,也就是被调用者取得程序的控制权,它必须做3件事:建立它自己的栈帧,为局部变量分配空间,最后,如果需要,保存寄存器EBX,ESI和EDI的值。
首先fun必须建立它自己的栈帧。EBP寄存器现在正指向main的栈帧中的某个位置,这个值必须被保留,因此,EBP进栈。然后ESP的内容赋值给了
EBP。这使得函数的参数可以通过对EBP附加一个偏移量得到,而栈寄存器ESP便可以空出来做其他事情。如此一来,几乎所有的c函数都由如下两个指令开
始:
push ebp
mov ebp, esp
下一步,fun必须为它的局部变量分配空间,同时,也必须为它可能用到的一些临时变量分配
空间。比如,foo中的一些C语句可能包括复杂的表达式,其子表达式的中间值就必须得有地方存放。这些存放中间值的地方同城被称为临时的,因为他们可以为
下一个复杂表达式所复用
现在,局部变量和临时存储都可以通过基准指针EBP加偏移量找到了。
最后,如果fun用到EBX,ESI和EDI寄存器,则它必须在栈里保存它们。

fun的函数体现在可以执行了。这其中也许有进栈、出栈的动作,栈指针ESP也会上下移动,但EBP是保持不变的。这意味着我们可以一直用[EBP+…]找到第一个参数,而不管在函数中有多少进出栈的动作。
函数fun的执行也许还会调用别的函数,甚至递归地调用foo本身。然而,只要EBP寄存器在这些子调用返回时被恢复,就可以继续用EBP加上偏移量的方式访问实际参数,局部变量和临时存储。
紧接着当被调用者执行完毕时将消除栈帧结构,调用pop指令。
在把程序控制权返还给调用者前,被调用者foo必须先把返回值保存在EAX寄存器中。其次,foo必须恢复EBX,ESI和EDI寄存器的值。进栈和出栈操作的次数必须保持平衡。

  
  在程序控制权返回到调用者main)后,这时,传递给fun的参数通常已经不需要了。我们可以把参数一起弹出栈,这可以通过把栈指针实现:
add esp, 8
此时fun函数调用结束栈帧结构恢复至图一。
如果在函数调用前,EAX,ECX和EDX寄存器的值被保存在栈中,调用者main函数现在可以把它们弹出。这个动作之后,栈顶就回到了我们开始整个函数调用过程前的位置。
这样整个函数的调用就结束了

C语言函数调用及栈帧结构的更多相关文章

  1. Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)

    Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...

  2. 深入理解java虚拟机(十) Java 虚拟机运行时栈帧结构

    运行时栈帧结构 栈帧(Stack Frame) 是用于虚拟机执行时方法调用和方法执行时的数据结构,它是虚拟栈数据区的组成元素.每一个方法从调用到方法返回都对应着一个栈帧入栈出栈的过程. 每一个栈帧在编 ...

  3. Windows x64 栈帧结构

    0x01 前言 Windows 64位下函数调用约定变为了快速调用约定,前4个参数采用rcx.rdx.r8.r9传递,多余的参数从右向左依次使用堆栈传递.本次文章是对于Windows 64位下函数调用 ...

  4. 图解JVM字节码执行引擎之栈帧结构

    一.执行引擎      “虚拟机”的概念是相对于“物理机”而言的,这两种“机器”都有执行代码的能力.物理机的执行引擎是直接建立在硬件处理器.物理寄存器.指令集和操作系统层面的:而“虚拟机”的执行引擎是 ...

  5. 详细解析Java虚拟机的栈帧结构

    欢迎关注微信公众号:万猫学社,每周一分享Java技术干货. 什么是栈帧? 正如大家所了解的,Java虚拟机的内存区域被划分为程序计数器.虚拟机栈.本地方法栈.堆和方法区.(什么?你还不知道,赶紧去看看 ...

  6. 【转载】深入理解Java虚拟机笔记---运行时栈帧结构

    栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作 ...

  7. 关于C语言函数调用压栈和返回值问题的疑惑

    按照C编译器的约定调用函数时压栈的顺序是从右向左,并且返回值是保存在eax寄存器当中.这个命题本该是成立的,下面用一个小程序来反汇编观察执行过程: #include<stdio.h> in ...

  8. C语言的函数调用过程(栈帧的创建与销毁)

    从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...

  9. c函数调用过程原理及函数栈帧分析

    转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...

随机推荐

  1. C++ static关键字

    一.面向过程中的static 1.修饰全局变量(静态全局变量) (1)静态全局变量在全局数据区分配内存: (2)未经初始化的静态全局变量会被程序自动初始化为0: (3)静态全局变量在申明它的整个文件是 ...

  2. snprintf()返回值的陷阱

    int snprintf(char *restrict buf, size_t n, const char * restrict  format, ...); 函数说明:最多从源串中拷贝n-1个字符到 ...

  3. 线程调度的问题:Lock Convoy(锁封护)与Priority Inversion(优先级反转)

    Lock Convoy(锁封护) [1]Lock Convoy是在多线程并发环境下由于锁的使用而引起的性能退化问题.当多个相同优先级的线程频繁地争抢同一个锁时可能会引起lock convoy问题,一般 ...

  4. iOS 第三方类库之MBProgressHUD

    github链接地址 MBProgressHUD是一个开源的第三方类库实现了很多种样式的提示框,类似Activity indicator,使用上简单.方便,并且可以对显示的内容进行自定义,功能很强大, ...

  5. Alert and Action sheets and Timer and Animation

  6. MySQL的索引知识

    一.什么是索引. 索引是用来加速查询的技术的选择之一,在通常情况下,造成查询速度差异 的因素就是索引是否使用得当.当我们没有对数据表的某一字段段或者多个 字段添加索引时,实际上执行的全表扫描操作,效率 ...

  7. 总线(bus);设备(devices);驱动(drivers)

    Linux Cross Reference Free Electrons Embedded Linux Experts • Source Navigation  • Diff Markup  • Id ...

  8. linux下ls出现文件的后缀有@,* ,/之类的解释

    ls -Fafptool*  img_maker*    lzcmp@     lzfgrep@   lzma*         lzmore*         node-pre-gyp@bower@ ...

  9. cocos2d-x游戏开发(一)之环境搭建篇

    前言 进入研究生生涯已经有一段时间,感觉却没做些什么,实验室虽有一个很大的国家项目,但考虑到它这么的单一,总想利用业余时间做些什么,拓宽一下自己的知识面. 偶然机会,了解到cocos这个东东,恰好,实 ...

  10. PowerShell-第3章 变量与对象

    3.1 在变量中存储信息 假如说变量存储了大量数据,且不用了,可以赋值为$null,则可以释放变量占用的内存空间. 3.2 访问环境变量 获得所有环境变量 Get-ChildItem env: 获得某 ...