工作随记之——ARM内存,程序空间及堆栈操作
- 格式:doc
- 大小:15.00 KB
- 文档页数:2
arm函数调用中的堆栈变化在计算机的运行过程中,函数调用是一种常见的操作。
当程序调用一个函数时,需要先将当前的运行状态(例如当前指令的地址、堆栈指针等)保存在堆栈中,然后跳转到函数中执行。
函数执行完毕后,再从堆栈中恢复之前的运行状态,继续执行原来的程序。
这个过程中,堆栈扮演了一个非常重要的角色。
本文将介绍在ARM架构中,函数调用时堆栈的变化。
1. 堆栈的基本概念在程序中,有一片内存区域被用来存放函数的局部变量和一些临时变量,称为堆栈。
堆栈是一个先进后出的数据结构,即最后存入的数据最先弹出。
当程序调用一个函数时,会在堆栈中分配一段空间来存放函数的参数、局部变量、返回地址等信息。
当函数返回时,这些信息会从堆栈中弹出,恢复程序之前的状态。
在ARM架构中,堆栈的地址是4字节对齐的,即堆栈指针(SP)的值必须是4的倍数。
这是因为ARM指令集中的大多数指令都是以4字节为单位的,如果SP不是4字节对齐的,那么执行指令时会出错。
2. 函数调用时堆栈的变化当程序调用一个函数时,堆栈的变化可以分为以下几个步骤:(1)保存寄存器在ARM架构中,函数调用过程中一些重要的状态信息通常保存在寄存器中。
为了不影响原程序的运行,需要在堆栈中保存这些寄存器的值。
这些寄存器包括:R0~R3(函数参数)、R14(LR,返回地址)、R13(SP,堆栈指针)、R11(FP,帧指针)等。
在进入函数之前,需要将这些寄存器的值压入堆栈中。
具体操作为:``` PUSH {R0-R3, R11, LR} ```以上指令将R0~R3、R11、LR的值压入堆栈中。
其中,LR保存的是返回地址,R11保存的是帧指针(Frame Pointer,FP)。
FP是一个指针,指向当前函数的栈帧,用于访问局部变量。
堆栈指针SP也需要被保存,但不是在这里保存,而是在进入函数之前保存。
(2)分配空间在堆栈中为当前函数分配空间,用于存放参数、局部变量和其它临时变量。
分配的空间的大小由当前函数所需的局部变量和参数决定。
arm堆栈操作arm堆栈的组织结构是满栈降的形式,满栈即sp是要停留在最后一个进栈元素,降:就是堆栈的增长方向是从高地址向低地址发展。
arm对于堆栈的操作一般采用LDMFD (pop)和STMFD (push) 两个命令。
以前困惑的就是STMFD 命令对于操作数是按照什么顺序压栈的比如:STMFD sp!{R0-R5,LR} 进栈顺序是:高地址(1方式)LRR5R4```````R0 <-sp低地址还是:高地址(2方式)R0R1```R5LR <-sp低地址现在通过下表,可以轻松的解决这个问题:按照图表,可知STMFD对应的是STMDB,根据arm指令手册,可知STMDB入栈顺序是(1方式)而LDMFD对应的是LDMIA,这样这两个操作就可以成功配对:以下是我在学习ARM指令中记录的关于堆栈方面的知识1、寄存器R13 在ARM 指令中常用作堆栈指针2、对于R13 寄存器来说,它对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:R13_<mode> 其中,mode为以下几种模式之一:usr、fiq、irq、svc、abt、und。
3、寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。
而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。
由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
4、有四种类型的堆栈:堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
51单片机堆栈操作指令的用法51单片机是一种非常常用的单片机芯片,其指令集非常丰富,其中包含了很多堆栈操作指令。
堆栈操作指令是用来进行数据的入栈和出栈操作的指令,通过堆栈操作指令,我们可以方便地保存和恢复程序执行中的临时数据,提高代码的灵活性和效率。
本文将详细介绍51单片机堆栈操作指令的用法,帮助读者更好地理解和运用这些指令。
一、堆栈简介堆栈(Stack)是一种特殊的数据结构,具有后进先出(LIFO)的特点。
在51单片机的内部RAM中,有一段专门用来存放堆栈的空间,这段空间的大小为128字节(地址为0x07Fh至0x080h)。
在程序执行过程中,我们可以通过堆栈操作指令将数据入栈或者出栈,进栈是将数据放入堆栈,出栈是将数据从堆栈中取出。
二、堆栈操作指令51单片机的指令集中包含了以下几条堆栈操作指令:1. PUSH 指令PUSH指令用于将8位数据入栈,将要入栈的数据放入寄存器A中,通过PUSH 指令可以将A的数据压入堆栈。
PUSH指令的实际操作是将A的数据先放入栈顶指针(SP)所指向的内存单元中,然后将SP的值减1,即栈顶指针向下移动一个位置。
2. POP 指令POP指令用于将数据出栈,即从堆栈中取出一个8位数据,并放入寄存器A中。
POP指令的实际操作是将栈顶指针向上移动一个位置,然后将栈顶指针所指向的内存单元中的数据取出,并放入A中。
3. XCH指令XCH指令用于交换A寄存器的数据和栈顶指针所指向的内存单元的数据。
具体操作是将栈顶指针所指向的内存单元中的数据取出,并放入A中,然后将A中的数据放回栈顶指针所指向的内存单元中。
4. LCALL指令LCALL指令是一个特殊的调用指令,用于将下一条指令的地址入栈,并转移到指定地址处执行。
具体操作是将下一条指令的地址(即当前指令的地址加3)入栈,然后将指定地址的值赋给程序计数器(PC)。
5. RET指令RET指令用于从子程序返回,从堆栈中取出地址,并赋给程序计数器(PC),从而实现返回到调用该子程序的地方继续执行。
mcu 内存和任务堆栈使用注意事项MCU(Microcontroller Unit)是一种集成了微处理器核心、存储器、外设和时钟电路等功能的单芯片微控制器。
在嵌入式系统开发中,MCU的内存管理和任务堆栈的使用是非常重要的,对系统性能和稳定性有着直接影响。
本文将探讨MCU内存和任务堆栈的使用注意事项。
一、MCU内存的使用注意事项1. 确保足够的内存空间:在开发嵌入式系统时,需要预留足够的内存空间来存储程序代码、数据和堆栈等。
如果内存空间不足,可能导致程序崩溃或无法正常运行。
2. 合理分配内存:在程序设计中,要合理分配内存空间,避免内存碎片的产生。
可以使用动态内存分配的方法,如malloc()函数来动态申请和释放内存。
3. 避免内存泄漏:内存泄漏是指程序在申请内存后,没有及时释放导致内存空间无法再次被利用的情况。
在编写程序时,应该注意及时释放不再使用的内存,避免内存泄漏问题的发生。
4. 内存访问的安全性:在进行内存操作时,要确保内存的访问是合法的,不会越界或访问未初始化的内存区域。
否则可能导致程序崩溃或数据错误。
5. 使用内存保护机制:一些MCU提供了内存保护机制,可以设置特定的内存区域为只读或只写,防止程序错误地修改或访问内存。
在开发过程中,可以使用这些机制来提高系统的稳定性和安全性。
二、任务堆栈的使用注意事项1. 合理设置堆栈大小:任务堆栈的大小应根据任务的复杂度和调用的函数数量来合理设置。
如果堆栈大小过小,可能导致堆栈溢出的问题;如果堆栈大小过大,则会占用过多的内存资源,浪费系统资源。
2. 避免递归调用:递归调用是指一个函数直接或间接地调用自身。
在嵌入式系统中,递归调用往往会消耗大量的堆栈空间,容易导致堆栈溢出。
因此,应尽量避免在嵌入式系统中使用递归调用。
3. 检测堆栈溢出:堆栈溢出是指堆栈空间被使用超过了其容量,导致数据覆盖或程序崩溃的情况。
为了检测堆栈溢出,可以在任务堆栈的最高地址处设置一个特殊的标记值,并定期检查该标记值是否被修改。
ARM堆栈方式
初学ARM 指令时,如果从字面上理解满堆栈和空堆栈很有可能会歪曲它
们的意思。
可以想象一下,满堆栈就是一个满的堆栈,不能再存储数据了;而空堆栈就是一个空的堆栈,没有被使用的堆栈,呵呵,这样理解的话那就错了。
实际上满堆栈和空堆栈确切的说应该是满栈或者是空栈,我们只是习惯了把栈叫做堆栈。
而二者深层的意思是说的位置或者地址,而不是堆栈。
如果叫满位置或者空位置更容易理解一些。
下面是《ARM 嵌入式系统开发--软件设计与优化》书中给的解释:
满堆栈(full stack,F)是指堆栈指针指向堆栈的最后一个已使用的地址或者满位置(也就是sp 指向堆栈最后一个数据项位置)。
空堆栈(empty stack,”E”)是指sp 指向堆栈的第一个没有使用的地址或者空位置(也就是说sp 指向堆栈最后一个数据项的下一个位置)。
满堆栈的关键词是最后一个已使用的地址,空堆栈是第一个没有使用的地址。
存储器堆栈可以分为两种:
一种是向上生长,就是向着高地址方向生长,称为递增堆栈。
一种是向下生长,就是向着低地址方向生长,称为递减堆栈。
这样,就有四种组合:满递增(FA)、空递增(EA)、满递减(FD)、空递减(ED)。
入栈规律:(1)满堆栈操作先调整SP,然后存入数据。
(2)空堆栈操作先存入数据,然后调整SP。
(3)递增堆栈调整SP 时,执行SP=SP+4(4)递减堆栈调整SP 时,执行SP=SP-4
出栈规律正好与入栈相反,也就是入栈的逆操作。
(1)空堆栈操作先调整。
gcc arm汇编堆栈定义在ARM汇编语言中,堆栈是一种非常重要的概念,用于保存程序执行过程中的临时数据和返回地址等信息。
在ARM架构中,堆栈是由系统自动管理的,而程序员主要需要了解如何定义和使用堆栈。
在ARM汇编中,堆栈的定义需要使用一对特殊的寄存器,分别为堆栈指针寄存器(Stack Pointer Register,SP)和基址寄存器(Frame Pointer Register,FP)。
SP用于保存堆栈的当前顶部地址,FP用于保存当前堆栈帧的基地址。
定义堆栈的关键指令是PUSH和POP,用于将数据从寄存器压入堆栈或从堆栈弹出到寄存器。
以下是一些常用的ARM汇编指令和相关定义:1. PUSH指令:将数据从寄存器压入堆栈,一般用于保存函数调用时需要保护的寄存器。
```PUSH {R0-R3} ; 将寄存器R0-R3的值从高地址向低地址依次压入堆栈```2. POP指令:将数据从堆栈弹出到寄存器,一般用于恢复函数调用前需要保护的寄存器。
```POP {R0-R3} ; 将堆栈中的值从低地址向高地址依次弹出到寄存器R0-R3```3. STMFD指令:将多个寄存器的值连续存储到堆栈中,以FP 为基地址。
```STMFD SP!, {R4-R7} ; 将寄存器R4-R7的值从高地址向低地址依次存储到以SP为基址的堆栈中,并更新SP的值```4. LDMFD指令:从堆栈中连续读取多个寄存器的值,以FP为基地址。
```LDMFD SP!, {R4-R7} ; 将以SP为基址的堆栈中的值从低地址向高地址依次读取到寄存器R4-R7,并更新SP的值```除了上述指令外,还可以使用MOV指令将堆栈指针寄存器的值保存到基址寄存器中,或将基址寄存器的值恢复到堆栈指针寄存器中。
在ARM汇编中,堆栈的定义和使用非常灵活,可以根据需要来决定何时保存和恢复寄存器的值。
在函数调用时,一般会先使用PUSH指令将需要保护的寄存器保存到堆栈中,然后再使用STMFD指令将更多的寄存器保存到堆栈中。
堆内存和栈内存详解(转载)堆:挨次随便栈:先进后出堆和栈的区分一、预备学问一程序的内存安排一个由C/C++编译的程序占用的内存分为以下几个部分1、栈区(stack)-由编译器自动安排释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈2、堆区(heap) ——般由程序员安排释放,若程序员不释放,程序结束时可能由OS回收。
留意它与数据结构中的堆是两回事,安排方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
-程序结束后有系统释放4、文字常量区一常量字符串就是放在这里的。
程序结束后由系统释放5、程序代码区一存放函数体的二进制代码。
二、例子程序这是一个前辈写的,特别具体//main, cppint a = 0;全局初始化区char *pl;全局未初始化区main ()(int b;栈char s[] = 〃abc";栈char *p2;栈char *p3 = "123456〃; 123456\0 在常量区,p3 在栈上。
static int c =0;全局(静态)初始化区pl = (char *)malloc(10);p2 = (char *)malloc(20);安排得来得10和20字节的区域就在堆区。
strcpy(pl, 〃123456〃); 123456\0放在常量区,编译器可能会将它与p3 所指向的〃 123456〃优化成一个地方。
}二、堆和栈的理论学问2. 1申请方式stack:由系统自动安排。
例如,声明在函数中一个局部变量int b;系统自动在栈中为b开拓空间heap:需要程序员自己申请,并指明大小,在c中malloc函数如pl=(char*)malloc(10);在 C++中用 new 运算符如 p2 = (char *)malloc (10);但是留意pl、 P2本身是在栈中的2申请后系统的响应栈:只要栈的剩余空间大于所申请空间,系统将为程序供应内存,否则将报特别提示栈溢出。
ARM程序装载和存储寄存器装载和存储LDMLDRSTMSTRSWP它们可能是能获得的最有用的指令。
其他指令都操纵寄存器,所以必须把数据从内存装载寄存器并把寄存器中的数据存储到内存中。
传送单一数据使用单一数据传送指令(STR 和LDR)来装载和存储单一字节或字的数据从/到内存。
寻址是非常灵活的。
首先让我们查看指令格式:LDR{条件} Rd, <地址>STR{条件} Rd, <地址>LDR{条件}B Rd, <地址>STR{条件}B Rd, <地址>指令格式这些指令装载和存储Rd 的值从/到指定的地址。
如果象后面两个指令那样还指定了‘B’,则只装载或存储一个单一的字节;对于装载,寄存器中高端的三个字节被置零(zeroed)。
地址可以是一个简单的值、或一个偏移量、或者是一个被移位的偏移量。
可以还可以把合成的有效地址写回到基址寄存器(去除了对加/减操作的需要)。
各种寻址方式的示例:译注:下文中的Rbase 是表示基址寄存器,Rindex 表示变址寄存器,index 表示偏移量,偏移量为 12 位的无符号数。
用移位选项表示比例因子。
标准寻址方式 - 用 AT&T 语法表示为 disp(base, index, scale),用 Intel 语法表示为 [base + index*scale + disp],中的变址(连带比例因子)与偏移量不可兼得。
STR Rd, [Rbase] 存储 Rd 到 Rbase 所包含的有效地址。
STR Rd, [Rbase, Rindex] 存储 Rd 到 Rbase + Rindex 所合成的有效地址。
STR Rd, [Rbase, #index] 存储 Rd 到 Rbase + index 所合成的有效地址。
index 是一个立即值。
例如,STR Rd, [R1, #16] 将把 Rd 存储到 R1+16。
STR Rd, [Rbase, Rindex]! 存储 Rd 到 Rbase + Rindex 所合成的有效地址,并且把这个新地址写回到 Rbase。
1、寄存器R13 在ARM 指令中常用作堆栈指针2、对于R13 寄存器来说,它对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:R13_<mode>其中,mode为以下几种模式之一:usr、fiq、irq、svc、abt、und。
3、寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。
而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。
由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
4、有四种类型的堆栈:堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending Stack)和递减堆栈(DecendingStack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。
这样就有四种类型的堆栈工作方式,ARM 微处理器支持这四种类型的堆栈工作方式,即:◎Full descending 满递减堆栈堆栈首部是高地址,堆栈向低地址增长。
栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。
ARM-Thumb过程调用标准和ARM、Thumb C/C++ 编译器总是使用Full descending 类型堆栈。
arm汇编栈指令Arm汇编栈指令是针对ARM处理器的汇编指令集中的一类指令。
ARM处理器是当今常见的一种芯片,在移动设备或者网络设备中使用广泛。
在ARM汇编语言中,栈指令被广泛应用,用来进行程序栈的操作。
本文将详细介绍ARM汇编栈指令,并分步骤介绍这类指令的使用。
一、什么是栈在程序设计中,栈是一种非常重要的数据结构。
它是一种特殊的数据结构,使用“后进先出”的原则来为操作提供支持。
在栈中,最后加入的数据项是第一个被取出的,而最先加入的数据项则是最后一个被取出的,这就是“后进先出”的原则。
同时,栈还支持两种基本操作PUSH和POP。
PUSH操作将数据项压入栈中,POP操作则是从栈中弹出最近压入的数据项。
二、栈指令的分类在ARM汇编语言中,栈指令通常分为两类: PUSH指令和POP指令。
PUSH指令用来把操作数压入栈中,而POP指令则是从栈中弹出操作数。
三、指令详解ARM处理器中的PUSH指令有以下几个指令:1. PUSH {registers}该指令会从指定的一组寄存器中把值压入到栈中。
例如,PUSH{r0-r3},就是把寄存器r0-r3中的值按递减的顺序压入到栈中。
2. PUSH {register_list}!该指令使用register_list中的寄存器将一个或多个寄存器的值压入到栈中。
在此指令中,感叹号!表示将程序栈指针(SP)向下移动。
而POP指令也有以下两个指令:1. POP {registers}该指令出栈指定的一组寄存器中的值,并将其写入寄存器。
例如,POP {r0-r3},就是从栈中递增取出值并将其写入寄存器r0-r3。
2. POP {register_list}!该指令从堆栈中出栈 register_list 中指定的寄存器,并将它们的值存储到 register_list 中指定的寄存器中。
四、使用方法在使用栈指令时,我们需要注意一些细节,下面我们以使用PUSH指令为例进行详细讲解。
ARM内存,程序空间及堆栈操作
1.ARM内存可任意读写,程序空间只可读不可写,不然会出现硬件错误。
(注意:在使用指针时,一定要分配空间,不然也会进入硬件异常)
unsigned int addr,temp,*data;
data=&temp;//重要
addr = 0x0000000; //地址可以是内存、程序空间、可读寄存器
*data=*(unsigned int*)addr;
*(unsigned int*)addr=*data; //适用于内存、可写寄存器,不可对程序空间进行此操作
2.内存堆栈的设置。
堆:程序执行后申请的内存空间。
栈:一般用作函数调用时,把返回信息压入栈中,函数执行完成后恢复现场。
(注意:在函数中使用变量,会消耗栈的空间,而不是堆的空间。
所以要合理设置变量跟栈空间大小。
)采用分散加载文件方式设置堆栈空间大小
HEAP +0x0 UNINIT //紧跟在程序初始化变量后面,最大空间利用。
向上增长
{
stack.o (heap)
}
STACK 0x10008000 UNINIT //内存的最高地址。
最后空间利用。
向下增长(LPC1766)
{
stack.o (stack)
}
heap.s stack.s文件:
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
AREA stack, DATA, NOINIT
user_stack ; SPACE 0X1000
AREA Heap, DATA, NOINIT
bottom_of_heap ; SPACE 0X4000
AREA rt_heap_stack, CODE, READONL Y
ENTRY
__user_initial_stackheap ;单区
LDR R0, = bottom_of_heap ;堆基地址
LDR R1, = user_stack ;栈基地址(内存最高地址,向下增长)
LDR R2, = user_stack ;堆极限地址
LDR R3, = bottom_of_heap ;栈极限地址
BX LR
ALIGN ;对齐
END。