我对寄存器、堆栈指针感到困惑,并让寄存器指向堆栈指针



有人能帮我解决这些问题吗?什么是堆栈指针?它有自己的地址吗?让寄存器指向堆栈指针意味着什么?

这里有一些我不理解的例子。

1.    STR R6, [R9, R8]

因此这一行假定使用R9作为基指针将R6的值存储到R8的地址。我将其解释为对基指针增加R8,将值存储在那里。但我不确定为什么R8是一个有效的增量。

2.    STR R1, [R0]
MOV R0, #3

我的老师说,在我们将R0赋值为3之后,这不会改变R0[SP]地址中存储的值。但我不明白RO和SP是如何相关的,它是如何工作的。

谢谢!

有人能帮我解决这些问题吗?

以下是一些背景:

为了提供一个函数调用另一个函数的能力(甚至像递归一样),现代处理器和操作系统协同工作,为程序提供线程或调用堆栈。

当一个函数调用另一个函数时,它有效地挂起自己,将处理器的控制权(即执行的指令流)转移到新函数,并等待它返回。一个挂起的函数在执行过程中被另一个函数的调用所中断。此外,新调用的函数被称为被激活,这意味着它们的局部变量开始存在,然后随着函数的返回(去激活)而离开。

调用堆栈方便了作为调用和返回的一部分所必需的挂起、激活和恢复。调用堆栈是一个逻辑数据结构,它包含两个值得注意的东西:

  • 内存区域,称为堆栈、运行时堆栈、线程堆栈或调用堆栈
  • 一个指示堆栈有多少正在使用与空闲/可用的指标,称为堆栈指针

堆栈指针是一个简单的可变全局变量。此全局变量包含一个值,该值从可用堆栈内存中描述正在使用的堆栈内存。它还指示下一次分配可以发生在哪里,以及堆栈内存中上一次分配发生在哪里——所有这些都在同一边界上。

函数的内部目的需要一些堆栈空间,例如存储局部变量、临时值和参数,尤其是当它们暂停调用另一个时,可以通过更改堆栈指针来轻松地从该堆栈数据结构中分配空间,它在逻辑上将一些内存从空闲移动到使用中,反之亦然——而不实际移动任何物理内存或移动内存值。

在程序开始时,堆栈指针全局变量在逻辑上指代堆栈内存区域的一端,表示堆栈内存全部可用,相应地,没有一个可用。

对于向下增长的堆栈,只需将堆栈指针全局变量的值递减,就可以将一些可用堆栈空间(至少在地址空间中已经存在的内存)逻辑地转移到正在使用的堆栈空间中递减量——就硬件而言,物理内存不一定会发生任何变化。

通过所有软件的同意,堆栈指针全局变量跟踪空闲调用堆栈内存和可用调用堆栈内存之间的边界,因此所有软件都承认堆栈指针上方引用的内存被认为是占用/使用/声明的(并且下面是可用/空闲的)。

作为真正的堆栈数据结构(LIFO),分配的堆栈内存只能以相反的分配顺序释放,例如通过增加堆栈指针全局变量。至关重要的是,当函数退出以返回其调用方时,它会将堆栈指针恢复到它进入时的值——从逻辑上释放它分配的任何堆栈空间,以便被返回的调用方找到它之前放置在堆栈中的自己的变量。

在将调用堆栈数据结构映射到物理硬件上时,堆栈数据结构的内存只映射到地址空间的一个区域——为调用堆栈数据架构保留的块,字节数组。堆栈指针全局变量是如此重要,以至于它通常被映射到一个专用的CPU寄存器,在ARM的情况下,就是sp寄存器。


什么是堆栈指针?

从逻辑上讲,它是一个全局可变变量,动态使用(并通过软件协议)来指示调用堆栈的可用内存和在用内存之间的边界。此逻辑变量具有到物理CPU资源sp寄存器的映射。

它有自己的地址吗?

不,通常情况下,变量太重要,无法存储在慢速内存中,因此它通常有自己的专用CPU寄存器。堆栈指针全局变量也是一个简单的标量值,因此可以很容易地存储在指针大小的任何CPU寄存器中。

由于只有内存有地址,所以包括sp在内的寄存器没有地址。

不需要指向堆栈指针变量——通常需要的是它的值,即该变量中的值,也就是指向已映射到堆栈的一些变量。

另一方面,堆栈内存本身需要寻址/可寻址/指向,在使用中,为了与内存保持一致,堆栈内存区域中的每个字节都有唯一的内存地址。

让寄存器指向堆栈指针意味着什么?

这是措辞错误的。堆栈指针全局变量作为调用堆栈数据结构的逻辑元素,映射到CPU寄存器的物理资源,即sp

堆栈指针变量位于sp寄存器中,保存与使用中的堆栈内存和可用堆栈内存之间边界处的内存地址相对应的无符号整数值或二进制位模式。当我们分配堆栈空间时,sp指向(保持的地址的数值)新分配的堆栈空间(由于堆栈向下增长,sp指向新分配空间的起始地址或最低地址)。

分配堆栈空间的函数在编译时不知道堆栈空间将在哪里,但在编译时知道其基于堆栈的变量相对于sp寄存器的位置(直到函数的动态激活才知道其值)。一旦函数被激活,它就可以计算其基于本地堆栈的变量的绝对地址(通过将sp中的值相加来编译时间常数)。

通过这种方式,递归函数可以被多次激活(在调用堆栈上多次激活),并且每个函数都有自己独立的局部变量。


(一些编译的代码或汇编代码也使用了帧指针的概念,这可能会使堆栈的解释复杂化,但只要说堆栈指针指的是新分配的堆栈空间的最低地址,而帧指针则指的是函数新分配的栈间空间的另一端就足够了。)

STR{cond}Rd,addressing_mode

STR(存储寄存器)指令将一个字从寄存器存储到addressing_mode计算的内存地址。

语法[Rn,+/-Rm]

此寻址模式通过将索引寄存器Rm的值与基址寄存器Rn的值相加或相减来计算地址。

比特就是比特,地址就是比特,数据就是比特,它只是比特,对我们来说没有任何意义。

堆栈就是我们用来快速分配和释放内存的内存,而无需系统调用。堆栈指针是"堆栈指针";指针";它是一个指向堆栈的地址。但你的问题中没有任何内容与堆栈有关。

STR R6, [R9, R8]

因此,它将R8和R9中的位相加,然后将其用作地址。然后,它将来自R6的比特存储到该地址的存储器中。

因此,如果r9是0x1000,如果r8是0x20,如果r6是0x12345678,则0x1234567将存储在从地址0x1020 开始的存储器中

[0x1020] = 0x78
[0x1021] = 0x56
[0x1022] = 0x34
[0x1023] = 0x12

你会在哪里用这个?

unsigned int data[16];
...
for(ra=0;ra<16;ra++) data[ra]=ra;

例如,你可以让r9指向数据,r8是一个索引,你可以把r9留在数据上,然后让r8变成0x00、0x04、0x08。。。

mov r9,#0x1000
mov r8,#0x00
str r8,[r9,r8]
add r8,r8,#4
str r8,[r8,r9]
add r8,r8,#4
...

注意,顺序无关紧要,我们在小学时就知道a+b=b+a

STR R1, [R0]
MOV R0, #3

因为这里没有上下文。R0是一个地址(它是位,但在执行str指令时,它是地址。R1位存储在该地址。

R0随后被改变为值3。这很好,寄存器就像变量,你可以将它们重新用于其他事情,这很可能就是这里发生的事情。

STR R6, [R9, R8]

R8和R9只有在程序员允许的情况下才有效。你是在暗示这里有bug吗?如果是这样,那么程序员在这些寄存器中使用了错误的值,或者出现了导致编译器放入错误值的错误。这里唯一真正错误的值是,如果R9+R8不是字对齐的地址,那么STR(字大小)将是未对齐的传输,这取决于处理器和设置,可能会导致故障。

堆栈也没有什么不同,在这种情况下,它是一个已知的寄存器,用于指向堆栈内存,但它以相同的方式使用,它包含位,在指令期间,这些位被认为是指向某个内存位置的地址,等等

最新更新