1.开篇

  本篇文章着重写的是系统中栈的工作原理,以及函数调用过程中栈帧的产生与释放的过程,有可能名字过大,如果不合适我可以换一个名字,希望大家能够指正,小丁虚心求教!如果有哪里写的不清楚的或者错误的地方请及时更正,小丁再次谢过了。文章里面有错别字,也可能会有好友说寄存器的32、16位的区别其实我感觉这里主要讲的还是些原理性的东西,后续会将文章图片错别字进行调整.(图片里面的posh改为push)

2.内存的不同用途

  根据不同的操作系统,一个进程可能被分配到不同的内存区域去执行。但是不管什么样的操作系统、什么样的计算机架构,进程使用的内存都可以按照功能大致分为以下4个部分:

  (1)代码区:这个区域存储着被装入执行的二进制机器代码,处理器会到这个区域取指并执行。

  (2)数据区:用于存储全局变量等。

  (3)堆区:进程可以在堆区动态地请求一定大小的内存,并在用完之后归还给堆区。动态分配和回收是堆区的特点。

  (4)栈区:用于动态地存储函数之间的关系,以保证被调用函数在返回时恢复到母函数中继续执行。

  在Windows平台下,高级语言写出的程序经过编译链接,最终会变成PE文件。当PE文件被装载运行后,就成了所谓的进程。

  PE文件代码段中包含的二进制级别的机器代码会被装入内存的代码区(.text),处理器将到内存的这个区域一条一条地取出指令和操作数,并送入运算逻辑单元进行运算;如果代码中请求开辟动态内存,则会在内存的堆区分配一块大小合适的区域返回给代码区的代码使用;当函数调用发生时,函数的调用关系等信息会动态地保存在内存的栈区,以供处理器在执行完被调用函数的代码时,返回母函数。

  如果把计算机看成一个有条不紊的工厂,我们可以得到如下类比:

    < CPU是完成工作的工人。

    < 数据区、堆区、栈区等则是用来存放原料、半成品、成品等各种东西的场所。

    < 存放在代码区的指令则告诉CPU要做什么,怎么做,到哪里去领原材料,用什么工具来做,做完以后把成品放到哪个货 仓去。

    < 值得一提的是,栈除了扮演存放原料、半成品的仓库之外,它还是车间调度主任的办公室。

3.栈与系统栈

  从计算机科学的角度来看,栈指的是一种数据结构,是一种先进后出的数据表。栈的最常见操作有两种:压栈(PUSH)、弹栈(POP);

  用于标识栈的属性也有两个:栈顶(TOP)、栈底(BASE)。

  栈在内存中的存放是高地址是栈底(Base),低地址是栈顶(Top)。

  下面来演示下栈的工作原理:

  首先我们先以这段汇编指令来进行操作:

[sql] view plaincopyprint?
  1. mov ax,0123H
  2. push ax
  3. mov bx 2244H
  4. push bx
  5. pop ax
  6. pop bx

mov ax,0123Hpush axmov bx 2244Hpush bxpop axpop bx

  首先我们先将10000H-1000FH这段内存空间来当做栈来使用,首先执行的操作是push ax,会将0123H压入到栈中,SP=SP-2,SS:SP指向当前栈顶当前的单元,以当前的单元为新的栈顶,将ax的数据送到SS:SP指向的内存单元中,SS:SP此时指向新的栈顶。此时ax的数值是0123H;详细请见下图

  接来下进行第二部操作:push bx,操作同上;

  接下来我们要演示的是pop操作,请注意pop操作的细节,比如到了栈底的时候指针是在哪里?这些都是要进行关注的。

  CPU执行pop ax时,SP=SP+2,SS:SP指向1000EH,pop操作栈顶元素,1000CH处的2266H依然存在,但是它在栈中不存在了,当再次push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,将其覆盖。详细看下图操作:

当再次进行pop给bx时,这是SP=SP+2,这时候指针就超出了栈底,就变成了SP=10H,所以我们得出一个结论就是当栈为空时,SS=1000H,SP=10H。详细看下面操作:

  内存的栈区实际上指的就是系统栈。系统栈由系统自动维护,它用于实现高级语言中函数的调用。对于类似C语言这样的高级语言,系统栈的PUSH、POP等堆栈平衡细节是透明的。一般说来,只有在使用汇编语言开发程序的时候,才需要和它直接打交道。

4.函数调用约定与相关指令

函数调用约定描述了函数传递参数方式和栈帧同工作的技术细节。不同的操作系统、不同的语言、不同的编译器在实现函数调用时的原理虽然基本相同,但具体的调用约定还是有差别的。这包括参数传递方式,参数入栈顺序是从右向左还是从左向右,函数返回时恢复堆栈平衡的操作在子函数中进行还是在母函数中进行。
  调用方式之间的差异

  具体的,对于Visual C++来说,可支持以下3种函数调用约定:

  

  如果要明确使用某一种调用约定,只需要在函数前加上调用约定的声明即可,否则默认情况下,VC会使用_stdcall的调用方式。 除了参数入栈方向和恢复栈平衡操作位置的不同之外,参数传递有时也会有所不同。例如,每一个C++类成员函数都有一个this指针,在Windows平台中,这个指针一般是用ECX寄存器来传递的,但如果用GCC编译器来编译,这个指针会作为最后一个参数压入栈中。

  注意:同一段代码用不同的编译选项、不同的编译器编译链接后,得到的可执行文件会有很多不同。

  函数调用大概包括以下几个步骤:

  (1)参数入栈:将参数从右向左依次压入系统栈中。

  (2)返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行。

  (3)代码区跳转:处理器从当前代码区跳转到被调用函数的入口处。

  (4)栈帧调整:具体包括:

  <1>保存当前栈帧状态值,已备后面恢复本栈帧时使用(EBP入栈)。

   <2>将当前栈帧切换到新栈帧(将ESP值装入EBP,更新栈帧底部)。

  <3>给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈顶)。

  <4>对于_stdcall调用约定,函数调用时用到的指令序列大致如下:

    push 参数3      ;假设该函数有3个参数,将从右向做依次入栈

    push 参数2

    push 参数1

    call 函数地址   ;call指令将同时完成两项工作:a)向栈中压入当前指令地址的下一个指令地址,即保存返回地址。 b)跳转到所调用函数的入口处。

    push  ebp        ;保存旧栈帧的底部

    mov  ebp,esp     ;设置新栈帧的底部 (栈帧切换)

    sub   esp,xxx     ;设置新栈帧的顶部 (抬高栈顶,为新栈帧开辟空间)

  函数返回的步骤如下:

  <1>保存返回值,通常将函数的返回值保存在寄存器EAX中。

  <2>弹出当前帧,恢复上一个栈帧。具体包括:

  (1)在堆栈平衡的基础上,给ESP加上栈帧的大小,降低栈顶,回收当前栈帧的空间。

  (2)将当前栈帧底部保存的前栈帧EBP值弹入EBP寄存器,恢复出上一个栈帧。

  (3)将函数返回地址弹给EIP寄存器。

  <3>跳转:按照函数返回地址跳回母函数中继续执行。

  还是以C语言和Win32平台为例,函数返回时的相关的指令序列如下:

  add esp,xxx     ;降低栈顶,回收当前的栈帧

  pop ebp         ;将上一个栈帧底部位置恢复到ebp

  retn            ;a)弹出当前栈顶元素,即弹出栈帧中的返回地址,至此,栈帧恢复到上一个栈帧工作完成。b)让处理器跳转到弹出的返回地址,恢复调用前代码区

5.寄存器与函数栈帧

  每一个函数独占自己的栈帧空间。当前正在运行的函数的栈帧总是在栈顶。Win32系统提供两个特殊的寄存器用于标识位于系统栈顶端的栈帧。

  (1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。

  (2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

  【寄存器对栈的标识作用见(图1)】

  函数栈帧:ESP和EBP之间的内存空间为当前栈帧,EBP标识了当前栈帧的底部,ESP标识了当前栈帧的顶部。

  在函数栈帧中,一般包含以下几类重要信息。

  (1)局部变量:为函数局部变量开辟的内存空间。

  (2)栈帧状态值:保存前栈帧的顶部和底部(实际上只保存前栈帧的底部,前栈帧的顶部可以通过栈帧平衡计算得到),用于在本栈被弹出后恢复出上一个栈帧。

  (3)函数返回地址:保存当前函数调用前的“断点”信息,也就是函数调用前的指令位置,以便在函数返回时能够恢复到函数被调用前的代码区中继续执行指令。

  注:函数栈帧的大小并不固定,一般与其对应函数的局部变量多少有关。函数运行过程中,其栈帧大小也是在不停变化的。除了与栈相关的寄存器外,我们还需要记住另一个至关重要的寄存器。

  EIP:指令寄存器(extended instruction pointer),其内存放着一个指针,该指针永远指向下一条等待执行的指令地址。 可以说如果控制了EIP寄存器的内容,就控制了进程——我们让EIP指向哪里,CPU就会去执行哪里的指令。这里不多说EIP的作用,我个人认为王爽老是的汇编里面讲EIP讲的已经是挺好的了~这里不想多写关于EIP的事情。

6.结束语

  本文是针对上面两篇文章的一个基础性的补充~希望大家能够喜欢和指正其中的不足之处。

函数调用 压栈的工作原理相关推荐

  1. 【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理

    栈基本工作原理 导航 深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈 深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第 ...

  2. c语言函数参数压栈,函数调用压栈 浅谈C语言函数调用参数压栈的相关问题

    想了解浅谈C语言函数调用参数压栈的相关问题的相关内容吗,在本文为您仔细讲解函数调用压栈的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:函数调用压栈,下面大家一起来学习吧. 参数入栈的顺序 ...

  3. CG-62 压电式雨量传感器 工作原理 使用安装环境 高精度

    产品概述 传感器由上盖.外壳和下盖组成,壳体内部有压电片和电路板,可以固定在外径50mm立柱上.传感器采用冲击测量原理对单个雨滴重量进行测算,进而计算降雨量.雨滴在降落过程中受到雨滴重量和空气阻力的作 ...

  4. 压电式压力传感器工作原理

    压电效应是压电压力传感器的主要工作原理,压电传感器不能用于静态测量,因为经过外力作用后的电荷,只有在回路具有无限大的输入阻抗时才得到保存.实际的情况不是这样的,所以这决定了压电传感器只能够测量动态的应 ...

  5. chroma负载机恒压工作原理_一款恒压/恒流充电器工作原理分析

    该充电器工作原理介绍如下,电路见附图所示. 1 .主电路 采用220V电网直接供电,经 KZ1 - KZ4 全控桥式整流,再经极性切换开关输出接负载 ( 蓄电池 ) .当蓄电池在充电工作方式时,切换开 ...

  6. c语言函数调用过程中栈的工作原理理解

    差不多每个程序员都知道,函数调用过程,就是层层入栈出栈的过程. 那么这个过程中的详细的细节是什么样子的呢? 阅读了以下几篇文章之后,对整个过程基本理解了: C函数调用过程原理及函数栈帧分析 阅读经典- ...

  7. 定压补水装置工作原理介绍

    一:定压补水装置原理 定压补水装置是国内近年来出现的新给水设备.隔膜式气压自动供水设备由膨胀水罐.水泵.自动控制柜.管道.阀门.仪表等组成.该产品可以弥补低压供水不足,代替屋顶水箱或水塔.该系列用于居 ...

  8. 压电雨量传感器工作原理介绍

    传感器采用冲击测量原理对单个雨滴重量进行测算,进而计算降雨量.雨滴在降落过程中受到雨滴重量和空气阻力的作用,到达地面时速度为恒定速度,根据P=mv,测量冲击即可求出雨滴重量,进而得到持续降雨量. 1. ...

  9. 恒压恒流电源的工作原理

    source: click here 一个直流电源有两种工作状态,一种是恒压状态,按照恒压电源的特征在工作,一种是恒流状态,按照恒流电源的特征在工作.这种电源内部有两个控制单元,一个是稳压控制单元,在 ...

最新文章

  1. 统计字符串中指定字符出现次数(Java)
  2. ajax实现php验证码验证码,PHP验证码之Ajax验证实现方法_PHP教程
  3. Ubuntu换源失败:Could not get lock /var/lib/apt/lists/lock - open
  4. Articles for objccn.io. objc.io的完整、准确、优雅的中文翻译版本 http://objccn.io/
  5. php正则表达式图谱
  6. 我发现了 Microsoft Azure 中的两个漏洞
  7. Swift高仿喜马拉雅APP之一Charles抓包、图片资源获取等
  8. 技术至简-8:调制解调到底是什么样的数学运算?---从时域和频域两个角度看调制解调的本质
  9. 密码中明文、密文、密钥关系的简单介绍
  10. Android体重档案代码,Android开发实现的标准体重计算器功能示例
  11. 易宝支付为二清机构放开通道,导致POS代理机构卷款跑路
  12. [网络安全学习篇3]:批处理、病毒、用户与组管理、破解系统密码(千峰网络安全视频笔记 3 day)
  13. CAD.net cad中的菜单创建
  14. 海洋主题绘画_儿童画创意 | 三个海洋主题课例分享,超有创意的教程
  15. 浏览器修改主页不失效,打开浏览器弹出首页修改方法
  16. 中国联通3g上网设置
  17. C# Task阻塞方式和延续操作
  18. 计算机二级VB语言程序设计-第九章
  19. ISO9000和ISO9001有哪些区别?
  20. 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第n个月的兔子对数为多少?

热门文章

  1. 连接编码器_编码器与PLC的接线
  2. java项目 配置文件_细数Java项目中用过的配置文件(properties篇)
  3. make j4什么意思_为什么天天坚持撸铁 肌肉增长不明显
  4. c语言安卓图形库cairo,cairo 图形库
  5. 做个全国一等奖的小车,其实不难(F题:智能送药小车方案分享)
  6. 基于VHDL的可变速彩灯控制器的设计
  7. 《c语言从入门到精通》看书笔记——第6章 选择结构程序设计
  8. 常见人名大全_生辰八字起名取名:2020年属鼠的女孩起名字大全
  9. 8086微型计算机原理答案,8086微型计算机原理与应用(吴宁)习题答案(第三章)
  10. ps4修改服务器地区,ps4怎么修改服务器地址