编译器是否对范围内的堆栈变量使用相对地址?
Is compiler using relative addresses for stack variables in the scope?
我已经为支持多任务处理的 STM32 (ARM) MCU 编写了一个任务调度程序。我可以创建并发任务,每个任务都有自己分配的堆栈,因为这是最直接的方法。这些堆栈分配在堆上。由于这些堆栈的大小是静态的,这是一种非常低效的内存使用方式space,我打算添加动态堆栈重新分配。
我的问题是:如果我在另一个内存地址上重新分配任务的堆栈并复制所有堆栈内容并更新任务(即堆栈指针),任务是否可以继续 运行 没有任何问题,如果我我没有在任务代码中使用堆栈的任何绝对地址吗? C 编译器是否仅在堆栈中使用相对寻址,即使我在作用域中获取一个变量的地址?
示例:
void A() {
int i = 0;
int* iPtr = &i;
}
在上面的例子中,iPtr
的值将是一个静态地址,还是像 currAddress-4
这样的相对地址? (如果我没有将它传递给另一个函数,只是在范围内使用它。)
那么编译器有没有办法在这个范围内使用带偏移量的相对地址,或者只使用变量的直接地址?
如果有相对地址处理,那么我可以在另一个内存上自由地重新分配堆栈 space,如果没有,那么我不能,这将是一个问题。
我真的没有找到任何关于此的好文档,所以也将不胜感激!
如果这不是正确的方法,那么以前如何实现任务的堆栈重新分配?
抱歉这么说,但指针始终是绝对的。您可以通过查看一个小程序的汇编来确认这一点,该程序接受一个指向局部变量的指针并将其传递给另一个函数:
void set(int *p) {
*p = 42;
}
void bar(void) {
int l;
set(&l);
}
编译为
set:
movs r3, #42
str r3, [r0] // store to absolute address in r0
bx lr
bar:
push {lr}
sub sp, sp, #12
add r0, sp, #4 // form absolute pointer to stack location [sp+4]
bl set
add sp, sp, #12
ldr pc, [sp], #4
https://godbolt.org/z/7EWoK3eda
这真的无法以任何其他方式工作:函数 set
必须编译成能够做正确事情的代码,无论它是传递一个指向堆栈、静态内存还是堆的指针。此外,当传递给具有不同堆栈帧的函数时,sp 相对指针甚至没有意义,因此 sp.
因此,如果您尝试将堆栈从 C 程序下移出,它几乎肯定会中断。
通常,避免在堆栈上浪费大量内存的方法是使用虚拟内存:为任务堆栈分配一大块 虚拟 地址 space .然后你将 physical 内存映射到其中的一小部分。随着堆栈增长并触及以前未使用的页面,MMU 发出页面错误信号,您的 OS 通过分配和映射另一页物理内存来处理该页面错误。因为虚拟页到物理页的映射是任意的,堆栈保持在一个固定的虚拟地址,而下面的物理内存不需要是连续的。它甚至可以在任务暂停时被复制和重新映射。
我已经为支持多任务处理的 STM32 (ARM) MCU 编写了一个任务调度程序。我可以创建并发任务,每个任务都有自己分配的堆栈,因为这是最直接的方法。这些堆栈分配在堆上。由于这些堆栈的大小是静态的,这是一种非常低效的内存使用方式space,我打算添加动态堆栈重新分配。
我的问题是:如果我在另一个内存地址上重新分配任务的堆栈并复制所有堆栈内容并更新任务(即堆栈指针),任务是否可以继续 运行 没有任何问题,如果我我没有在任务代码中使用堆栈的任何绝对地址吗? C 编译器是否仅在堆栈中使用相对寻址,即使我在作用域中获取一个变量的地址?
示例:
void A() {
int i = 0;
int* iPtr = &i;
}
在上面的例子中,iPtr
的值将是一个静态地址,还是像 currAddress-4
这样的相对地址? (如果我没有将它传递给另一个函数,只是在范围内使用它。)
那么编译器有没有办法在这个范围内使用带偏移量的相对地址,或者只使用变量的直接地址?
如果有相对地址处理,那么我可以在另一个内存上自由地重新分配堆栈 space,如果没有,那么我不能,这将是一个问题。
我真的没有找到任何关于此的好文档,所以也将不胜感激!
如果这不是正确的方法,那么以前如何实现任务的堆栈重新分配?
抱歉这么说,但指针始终是绝对的。您可以通过查看一个小程序的汇编来确认这一点,该程序接受一个指向局部变量的指针并将其传递给另一个函数:
void set(int *p) {
*p = 42;
}
void bar(void) {
int l;
set(&l);
}
编译为
set:
movs r3, #42
str r3, [r0] // store to absolute address in r0
bx lr
bar:
push {lr}
sub sp, sp, #12
add r0, sp, #4 // form absolute pointer to stack location [sp+4]
bl set
add sp, sp, #12
ldr pc, [sp], #4
https://godbolt.org/z/7EWoK3eda
这真的无法以任何其他方式工作:函数 set
必须编译成能够做正确事情的代码,无论它是传递一个指向堆栈、静态内存还是堆的指针。此外,当传递给具有不同堆栈帧的函数时,sp 相对指针甚至没有意义,因此 sp.
因此,如果您尝试将堆栈从 C 程序下移出,它几乎肯定会中断。
通常,避免在堆栈上浪费大量内存的方法是使用虚拟内存:为任务堆栈分配一大块 虚拟 地址 space .然后你将 physical 内存映射到其中的一小部分。随着堆栈增长并触及以前未使用的页面,MMU 发出页面错误信号,您的 OS 通过分配和映射另一页物理内存来处理该页面错误。因为虚拟页到物理页的映射是任意的,堆栈保持在一个固定的虚拟地址,而下面的物理内存不需要是连续的。它甚至可以在任务暂停时被复制和重新映射。