本文引用地址:http://www.eepw.com.cn/article/201611/321963.htm

韦东山:https://blog.csdn.net/thisway_diy/article/details/76422943


堆栈是一种具有“后进先出”(LIFO---Last In First Out)特殊访问属性的存储结构。堆

栈一般使用RAM 物理资源作为存储体,再加上LIFO 访问接口实现。

堆栈的实现方法:

随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为‘压栈’(push )。通常用一个指针(堆栈指针 SP---StackPointer)实现做一次调整,SP总指向最后一个压入堆栈的数据所在的数据单元(栈顶)。从堆栈中读取数据时,按照堆栈 指针指向的堆栈单元读取堆栈数据,这个过程叫做 ‘弹出’(pop ),每弹出一个数据,SP 即向相反方向做一次调整,如此就实现了后进先出的原则。

堆栈是计算机中广泛应用的技术,基于堆栈具有的数据进出LIFO特性,常应用于保存中断断点、保存子程序调用返回点、保存CPU现场数据等,也用于程序间传递参数。

ARM处理器中通常将寄存器R13作为堆栈指针(SP)。ARM处理器针对不同的模式,共有 6 个堆栈指针(SP),其中用户模式和系统模式共用一个SP,每种异常模式都有各自专用的R13寄存器(SP)。它们通常指向各模式所对应的专用堆栈,也就是ARM处理器允许用户程序有六个不同的堆栈空间。这些堆栈指针分别为R13、R13_svc、R13_abt、R13_und、R13_irq、R13_fiq,如表2-3堆栈指针寄存器所示。

为了更准确地描述堆栈,根据“压栈”操作时堆栈指针的增减方向,将堆栈区分为‘递增堆栈’(SP 向大数值方向变化)和‘递减堆栈’(SP 向小数值方向变化);又根据SP 指针指向的存储单元是否含有堆栈数据,又将堆栈区分为‘满堆栈’(SP 指向单元含有堆栈有效数据)和‘空堆栈’(SP 指向单元不含有堆栈有效数据)。

这样两两组合共有四种堆栈方式——满递增、空递增、满递减和空递减。

ARM处理器的堆栈操作具有非常大的灵活性,对这四种类型的堆栈都支持。

ARM处理器中的R13被用作SP。当不使用堆栈时,R13 也可以用做通用数据寄存器。

栈的整体作用:

1. 保护现场

现场/上下文相当于案发现场,总有一些案发现场,要记录下来,否则被别人破坏,便无法恢复。而此处说的现场,是指CPU运行时,用到的一些寄存器,比如r0,r1等,对于这些寄存器的值,如果不保存而直接跳转到子函数中执行,其很可能被破坏,因为其函数执行也要用到这些寄存器。因此,在函数调用之前,应该将这些寄存器等现场暂时保存(入栈push),等调用函数执行完毕后出栈(pop)再恢复现场。这样CPU就可以正确的继续执行了。

保存寄存器的值,一般用push指令,将对应的某些寄存器的值,一个个放到栈中,即所谓的压栈。然后待被调用的子函数执行完毕后再调用pop,把栈中的一个个的值,赋值给对应的那些你刚开始压栈时用到的寄存器,把对应的值从栈中弹出去,即所谓的出栈。

其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,之前的pc值存在lr中),在子程序执行完毕后,再把栈中的lr值pop出来,赋值给pc,这样就实现了子函数的正确的返回。

2. 传递参数

C语言函数调用时,会传给被调用函数一些参数,对于这些C语言级别参数,被编译器翻译成汇编语言时,要找个地方存放下来,并且让被调用函数能访问,否则没法传递。找个地方存放下来分2种情况。一是,本身传递的参数不多于4个,可以通过寄存器传送。因为在前面的保存现场动作中,已经保存好对应的寄存器的值,此时这些寄存器是空闲的,可以供我们使用存放参数。二是,参数多于4个,寄存器不够用,就得用栈。

3. 临时变量保存在栈中

这些临时变量包括函数的非静态局部变量以及编译器自动生成的其他临时变量

举例分析C语言函数调用如何使用栈

上面的解释有些抽象,此处再用例子简单说明一下,就容易明白了:

用arm-inux-objdump –d u-boot dump_u-boot.txt得到dump_u-boot.txt文件。该文件是包含了u-boot可执行汇编代码,从中我们可以看到相应C程序对应的汇编代码。

下面贴出两个函数的汇编代码,一个是clock_init,另一个是与clock_init在同一C源文件中的函数CopyCode2Ram:

33d0091c: CopyCode2Ram:
33d0091c: e92d4070  push   {r4, r5, r6, lr}
33d00920: e1a06000  mov r6, r0
33d00924: e1a05001  mov r5, r1
33d00928: e1a04002  mov r4, r2
33d0092c: ebffffef  bl  33d008f0 b BootFrmNORFlash
......
33d00984: ebffff14  bl  33d005dc nand_read_ll
......
33d009a8: e3a00000  mov r0, #0 ; 0x0
33d009ac: e8bd8070  pop {r4, r5, r6, pc}
33d009b0:clock_init:
33d009b0: e3a02313  mov r2, #1275068416   ;0x4c000000
33d009b4: e3a03005  mov r3, #5 ; 0x5
33d009b8: e5823014  str r3,
......
33d009f8: e1a0f00e  mov pc, lr

(1) 先分析clock_init对应的汇编代码,可以看到该函数第一行 
:33d009b0: e3a02313 mov r2, #1275068416 ;0x4c000000 
没有我们期望的push指令,即没有将一些寄存器的值放入栈。这是因为,clock_init用到的r2,r3等寄存器,和前面调用clock_init前用到的寄存器r0,没有冲突,故此处不用push保存,有个寄存器要注意,r14,即lr,前面调用clock_init时,用的bl指令,所以会自动把跳转时的pc值赋值给lr,所以也不需要push将PC值保存到栈。而clock_init对应的汇编代码最后一行: 33d009f8: e1a0f00e mov pc, lr 就是我们常见的mov pc,lr,把lr值,即之前保存的函数调用时的PC值,赋值给现在的PC,这样便实现了函数的正确返回,即返回到了函数调用时下一个指令的位置。CPU可以继续执行原先函数内剩下的代码。

(2) CopyCode2Ram对应汇编代码第一行:33d0091c: e92d4070 push {r4, r5, r6, lr} 
就是我们所期望的,用push保存r4,r5,r6,lr,是因为此函数还包括其他函数调用 
:33d0092c: ebffffef bl 33d008f0 b BootFrmNORFlash…… 
33d00984: ebffff14 bl 33d005dc nand_read_ll 
…… 
也用到bl指令,会改变我们最开始进入clock_init时的lr值,所以也要push暂时保存起来。

而对应地,CopyCode2Ram最后一行:33d009ac: e8bd8070 pop {r4, r5, r6,pc}是把之前push的值给pop出来,还给对应的寄存器,其中最后一个是将开始push的lr的值pop出来赋给PC,实现了函数的返回。另外我们注意到,CopyCode2Ram的倒数第二行:33d009a8: e3a00000 movr0, #0 ; 0x0 是把0赋值给r0寄存器,就是我们说的返回值的传递,此处的返回值为0,也对应着C代码中的“ return 0”。

当然也可以用其他暂时空闲没有用到的寄存器来传递返回值。

对于使用哪个寄存器来传递返回值,是根据ARM的APCS寄存器的使用约定而设计的,最好按照其约定的来处理,不要随便改变它。这样程序将更加规范。

堆栈指针寄存器 SP详解以及栈的作用相关推荐

  1. 3.堆栈指针寄存器 SP 详解

    堆栈指针寄存器 SP 详解 堆栈是一种具有"后进先出"(LIFO---Last In First Out)特殊访问属性的存储结构.堆栈一般使用RAM 物理资源作为存储体,再加上LI ...

  2. 堆栈指针寄存器-SP详解

    基本概念: 堆栈指针寄存器-SP是什么? 答:堆栈是计算机存储数据的一种数据结构,SP的作用就是指示当前要出栈或入栈的数据,并在操作执行后自动递增或递减. 至于是入栈递增还是入栈递减,就是由CPU的生 ...

  3. ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR

    参考:堆栈指针寄存器 SP详解以及栈的作用 作者:蓝色鲜橙多 网址:https://blog.csdn.net/qq_36588941/article/details/89873633?utm_sou ...

  4. TI Cotex M3/4单片机关于寄存器操作详解

    TI Cotex M3/4单片机关于寄存器操作详解 前备知识 寄存器与偏移量 位带操作.位带区 TI单片机寄存器操作详解 快速在TI的手册里面确认某外设某寄存器的确切地址 寄存器操作介绍 本文参考 前 ...

  5. this指针的用法详解

    C++中this指针的用法详解 2010-11-12 20:40:45 分类: C/C++ this指针的用处: 一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果.th ...

  6. c语言二级指针有什么作用,C语言中二级指针的实例详解

    C语言中二级指针的实例详解 C语言中二级指针的实例详解 用图说明 示例代码: #include int main(int argc, const char * argv[]) { // int a = ...

  7. 51单片机的堆栈指针(SP)

    堆栈指针(SP,Stack Pointer),专门用于指出堆栈顶部数据的地址. 那么51单片机的堆栈在什么地方呢?由于单片机中存放数据的区域有限,我们不能够专门分配一块地方做堆栈,所以就在内存(RAM ...

  8. 【C语言】小妹不懂指针和数组的关系?那就安排指针数组关系详解

    目录 前言 一.什么是数组 二.什么是指针 三.指针变量的大小 四.数组和指针的关系 五.指针变量的自增自减运算 六.两个参数确定一个数组 七.字符型指针和字符型数组 总结 写在最后 前言 前段时间整 ...

  9. C语言(函数指针数组)详解

    要了解函数指针数组,可以从三个角度来分析.所谓函数指针数组,从字面意思上来解析,函数指针数组的组成有三个点,函数,指针,数组.首先我们知道,函数指针数组,是一个数组,数组的每个元素是函数指针,也就是一 ...

最新文章

  1. NIOS II 创建示例设计_Quartus II 9.0
  2. tia v15 添加项目_西门子S7-1500plc与S7-300plcPN/IO设备通信-创建项目
  3. java做a_Java编程实现A*算法完整代码
  4. 1、docker容器技术基础入门
  5. 老子《道德经》第三十五章
  6. 【前端面试】HTML5+CSS3初级面试1
  7. SpringBoot 配置记录
  8. java开发引擎_【java规则引擎】java规则引擎搭建开发环境
  9. ie的window.open 未指明的错误_快递问题公告中错误使用“黑恶势力”等言辞 青岛工学院致歉...
  10. bzoj2286 [Sdoi2011]消耗战 单调栈+lca
  11. [译]AppExtension编程指南:扩展基础1
  12. 小程序页面转pdf时如何实现自动分页
  13. Jason表情包在线生成,王境泽表情包,为所欲为表情包,窃格瓦拉表情包,在线生成
  14. Win10 关闭屏幕旋转(转向)
  15. 你,为什么需要加入一个Python社群呢?
  16. Java培训哪个机构比较好?怎么选?
  17. JavaScript 坦克大战
  18. hand crafted feature:histogram(直方图)
  19. 中国宠物协会会员查询介绍
  20. eeglab中文教程系列(1)-加载、显示数据

热门文章

  1. 中科大科学岛计算机复试,2020年中国科学技术大学研究生院科学岛分院复试办法及复试内容...
  2. FPGA基础设计(二):PS2键盘控制及短按、长按
  3. ps2口键盘改usb计算机设置,PS2接口键盘、鼠标改为USB接口
  4. linux常用命令_Linux常用命令~~~
  5. 面试题:数据库优化的方法
  6. 记一次遇到挖矿程序的经历
  7. 11. 形态学膨胀、腐蚀、开运算、闭运算
  8. DOS攻击和DDOS攻击之间有什么区别?
  9. 证明四元数表示旋转的过程实部为0,虚部为罗德里格斯公式结果
  10. php三元运算符要多个赋值,php中三元运算符用法