1. 背景
相信不少的小伙伴一直有这个问题,堆栈堆栈,到底在哪里?本文以RT-thread 为例,讲解 RTT 中堆栈的位置。
2. 栈在哪,堆在哪?
在回答堆栈在哪之前,首先要明白堆栈是内存,内存是什么?是实际的存储设备,比如 eeprom、flash、sram、sdram。我们的代码以及数据不可能没地方放,否则 cpu 怎么去执行指令。
cpu 需要从内存中读取数据到 cpu内部(寄存器),他怎么知道把数据放入内部那个寄存器呢?汇编代码中说明了数据存入那些寄存器中。
栈不需要程序员手动去申请,函数的局部变量、入参都占用栈空间,当函数执行结束是,栈空间会自动释放。
堆需要程序员手动申请、释放。
直接说明,堆栈在 ram 中。
3. 代码分析
话不多说,直接上代码
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int Image$$RW_IRAM1$$ZI$$Limit; //RW_IRAM1 这块内存中的 ZI 段的结尾
#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)// 堆的起始位置
#define HEAP_END STM32_SRAM_END // 堆的结束位置
从以上代码中抛出问题:
-
extern int Image$$RW_IRAM1$$ZI$$Limit
; 为何是 ZI 段的结尾是堆的起始地址?
在解释上述问题之前首先要知道 映像文件的组成以及 __main 函数的作用,点我查看。
相信看完之后就会明白 为何要到 ZI 段的结尾去定义堆区的起始地址。
void rt_system_heap_init(void *begin_addr, void *end_addr) // 初始化系统堆
{
/* initialize a default heap in the system */
rt_memheap_init(&_heap,
"heap",
begin_addr,
(rt_uint32_t)end_addr - (rt_uint32_t)begin_addr);
}
初始化好系统堆之后再看 rt_malloc
是怎么实现的
void *rt_malloc(rt_size_t size)
{
void *ptr;
/* try to allocate in system heap */
ptr = rt_memheap_alloc(&_heap, size);//申请内存
if (ptr == RT_NULL)//如果申请不到了
{
struct rt_object *object;
struct rt_list_node *node;
struct rt_memheap *heap;
struct rt_object_information *information;
/* try to allocate on other memory heap */
information = rt_object_get_information(RT_Object_Class_MemHeap);//查找这个类型的对象
RT_ASSERT(information != RT_NULL);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)//遍历对象链表中所有的节
{
object = rt_list_entry(node, struct rt_object, list);
heap = (struct rt_memheap *)object;
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
/* not allocate in the default system heap */
if (heap == &_heap)
continue;
ptr = rt_memheap_alloc(heap, size);//动态申请内存的核心,细节查看源码。
if (ptr != RT_NULL)
break;
}
}
}
.
.
.
.
.
return ptr;
}
以上看出 RTT 中允许存在多个内存块当作系统堆,这就方便的解决的 ram 不连续的问题。
从以上代码可以看出,堆区存在 ram 中。
4. RTOS 中的线程栈
rtthread 中创建一个线程必须需要申请一段栈空间,当然可以静态创建与动态创建,显然,动态创建中是调用了 rt_malloc 函数申请一块空间用来当作栈,因此,动态创建的栈一定与堆区在同一空间。静态创建是事先搞一块内存,通常创建一个数组,数据的空间显然是连续的一块空间,而数组占用的地址到底在哪?我们不妨打印出来或者查看 map 文件,通过查看 map 文件我们发现,栈也在 ram 中。
为什么每个线程都要有独立的线程栈?因为这个线程随时可能被切走,当返回来继续执行时,之前的运算产生的中间结果是需要恢复到被切走那一瞬间的,如果没有栈,这些中间结果保存在哪里?中间结果保存在 CPU 寄存器里,而另外一个线程也要用到 CPU 寄存器,因此,CPU 寄存器中的运算结果需要保存到栈中去。