系统栈的工作原理(转)

 

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)。

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

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

mov ax,0123H

push ax

mov bx 2244H

push bx

pop ax

pop 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.结束语

  本文是针对上面两篇文章的一个基础性的补充~希望大家能够喜欢和指正其中的不足之处,小丁虚心学习于请教~不知道名字叫啥~

  内容参考:0day安全:软件漏洞分析技术(第2版)

 

http://www.cnblogs.com/dwlsxj/p/Stack.html

 

时间: 2024-09-18 21:33:49

系统栈的工作原理(转)的相关文章

让你提前认识软件开发(52):系统某模块工作原理详述

第3部分 软件研发工作总结 系统某模块工作原理详述   [文章摘要]         某模块在系统中占有非常重要的地位,该模块能够对符合条件的动态信箱进行清理.本模块直接清理的信箱包括:过期动态信箱.冷冻信箱和空动态信箱:删除非动态信箱由本模块发送消息到其它模块完成.         本文对该模块的工作原理的详细介绍,为相关模块的开发和测试提供了有益的参考,同时也有利于现场人员对本模块进行维护.   [关键词]         系统  模块  数据库  流程   1. 本模块删除的信箱类型   

深入了解SQL Server系统数据库工作原理

数据库管理员(DBA)的一项基本的技能是对SQL数据库引擎的系统数据库的深刻理解.数据库开发人员了解SQLSERVER自带的系统数据库也是十分有用的.下面就列出了其中的一些系统数据库.(注:如果你决定研究一下这些系统数据库,那么你需要有一个开发数据库.) Master Master数据库保存有放在SQLSERVER实体上的所有数据库,它还是将引擎固定起来的粘合剂.由于如果不使用主数据库,SQLSERVER就不能启动,所以你必须要小心地管理好这个数据库.因此,对这个数据库进行常规备份是十分必要的.

Android系统Recovery工作原理之使用update.zip升级过程---updater-script脚本语法简介以及执行流程(转)

  目前update-script脚本格式是edify,其与amend有何区别,暂不讨论,我们只分析其中主要的语法,以及脚本的流程控制. 一.update-script脚本语法简介:           我们顺着所生成的脚本来看其中主要涉及的语法.         1.assert(condition):如果condition参数的计算结果为False,则停止脚本执行,否则继续执行脚本.         2.show_progress(frac,sec):frac表示进度完成的数值,sec表示整

OAuth工作原理随想——让你的系统提供的服务更加安全

最近这段时间,一直都在和web服务打交道.自己项目组的系统需要别的项目组提供服务接口:别的平台(手机)平台又需要我们这边给它们提供接口.实现.调用.接口文档都有所涉及.从中我发现一个非常重要的问题--安全,这是一个被严重忽略的问题. 我认为在网络这个充满敌意的大环境下,应用和服务的安全性,是一个不得不重视的问题.去年年底的CSDN账号泄露以及口令明文的事件,至少给了企业两个最基本的警示:(1)不要等到出现问题之后,才知道要去挽救,在这个浮躁的社会氛围下,出现哪怕不是什么大问题,都会被群起而攻之:

Uber首席系统架构师Matt Ranney:可伸缩的软件系统工作原理

据报导,在短短四年间,Uber已经惊人地增长了38倍.现在,Uber的首席系统架构师Matt Ranney 在他的报告"可伸缩Uber实时市场平台"中,对Uber软件系统的工作原理进行了一个有趣而又详细的介绍. 如果你对Uber迅猛增长的单价感兴趣,这个并没有在报告中涉及.但是我们可以了解Uber的调度系统,怎样实行地理空间索引,怎样规划他们的系统,怎样实行高利用率和怎样处理失败,包括令人惊讶的方式处理数据中心故障,使用驱动的手机作为恢复外部分布式存储系统. 在Matt的报告中,给人印

init系统中Upstart简介及工作原理说明

本文简要介绍了这三种 init 系统的使用和原理,每个 Linux 系统管理员和系统软件开发者都应该了解它们,以便更好地管理系统和开发应用.本文是系列的第二部分,主要讲述 UpStart 的特点和使用.假如您使用的 Linux 发行版是 Ubuntu,很可能会发现在您的计算机上找不到/etc/inittab 文件了,这是因为 Ubuntu 使用了一种被称为 upstart 的新型 init 系统. 开发 Upstart 的缘由 大约在 2006 年或者更早的时候, Ubuntu 开发人员试图将

《SAP从入门到精通》——1.3 SAP R/3系统工作原理

1.3 SAP R/3系统工作原理 SAP R/3系统中的S代表系统(Systems),A代表应用软件(Applications),P代表产品(Products).R/3系统意味着这是SAP软件第3次发布的版本.用一句话来概括,SAP R/3系统是SAP公司开发的客户机/服务器环境下的套装软件,可用于处理一个公司中几乎所有的经营管理任务,常见的诸如发票支付.生产资源的管理.财务控制等都包含在其中. 为了处理各种各样的管理任务,R/3系统必须是一个十分复杂的程序.然而,用户会发现,R/3系统中包含

操作系统课堂笔记(3)SPOOLing系统工作原理与系统调用

SPOOLing系统工作原理 含义: 同时的外围设备联机操作(假脱机技术) 包括: –输入程序模块(预输入进程) –作业调度程序(作业调度进程) –作业控制程序(作业控制进程) –输出程序模块(缓输出进程) •作业执行前用慢速设备将作业预先输入到后援存储器(如磁盘.磁鼓,称为输入井)中,称为预输入 •作业运行中,当要使用数据时,直接从输入井中取出 •作业运行中,需要输出数据时,不必直接启动外部设备输出数据,只需将这些数据写入输出井中 •作业全部运行完毕,再从外部设备输出全部数据和信息,称为缓输出

Android中新引进的Google Authenticator验证系统工作原理浅析

为了改进Android的安全问题,Google在Android系统中引入了谷歌验证应用(Google Authenticator)来保证账号的安全.谷歌验证应用的使用方法是:用户安装手机客户端,生成临时身份验证码,提交到服务器验证身份,类似的验证系统还有Authy.Robbie在其GitHub页面发布了自己用Go语言实现的版本,并撰写了一篇博文来解释其工作原理. 通常来讲,身份验证系统都实现了基于时间的一次性密码算法,即著名的TOTP(Time-Based One-Time Password).