嵌入式面试八股文(十二)·FreeRTOS中·堆和栈
目录
1. 堆和栈
1.1 堆
1.2 栈
1.3 堆和栈的区别
1.3.1 分配方式
1.3.2 分配效率
1.3.3 生长方向
1.3.4 空间管理
1.3.5 存放内容
1. 堆和栈
1.1 堆
堆是一块用于动态分配内存的区域,用于存储程序运行时动态创建的对象。堆的大小可以在程序运行时动态调整。
堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收。
堆的分配和释放需要调用相应的函数,如malloc()和free函数。
在FreeRTOS中堆用来分配任务、队列、信号量、互斥量等内核对象的内存。
FreeRTOS 提供了 5 种堆管理实现(heap_1 到 heap_5),开发者可通过 FreeRTOS/Source/portable/MemMang 目录下的文件选择。
堆大小:在 FreeRTOSConfig.h 中通过 configTOTAL_HEAP_SIZE 定义(单位:字节)。
void *pvPortMalloc(size_t xSize); // 分配内存
void vPortFree(void *pv); // 释放内存
堆的内存地址生长方向与栈相反,由低到高,但需要注意的是,后申请的内存空间并不一定在先申请的内存空间的后面,即 p2 指向的地址并不一定大于 p1 所指向的内存地址,原因是先申请的内存空间一旦被释放,后申请的内存空间则会利用先前被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系。堆中存储的数据若未释放,则其生命周期等同于程序的生命周期。
int main() {// C 中用 malloc() 函数申请char* p1 = (char *)malloc(10);cout<<(int*)p1<<endl; //输出:00000000003BA0C0// 用 free() 函数释放free(p1);// C++ 中用 new 运算符申请char* p2 = new char[10];cout << (int*)p2 << endl; //输出:00000000003BA0C0// 用 delete 运算符释放delete[] p2;
}
1.2 栈
栈是一种用于存储局部变量和函数调用信息的数据结构。栈的大小在程序编译时确定,通常较小。
栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。参考如下代码:
int main() {int b; //栈char s[] = "abc"; //栈char *p2; //栈
}
栈的内存地址生长方向与堆相反,由高到底,所以后定义的变量地址低于先定义的变量,比如上面代码中变量 s 的地址小于变量 b 的地址,p2 地址小于 s 的地址。
栈中存储的数据的生命周期随着函数的执行完成而结束。
在FreeRTOS中,栈大小:在创建任务时通过 usStackDepth 参数指定(单位:sizeof(StackType_t))。
xTaskCreate(TaskFunction, "Task", usStackDepth, NULL, priority, &Handle);
栈的分配方式有两种类型:
- 静态栈:预先分配固定大小的数组(StaticTask_t)。
- 动态栈:从堆中动态分配内存(默认方式)。
FreeRTOS菜鸟入门(七)·创建任务·静态任务创建-CSDN博客
FreeRTOS菜鸟入门(八)·创建任务·动态任务创建_freertos 创建动态任务的堆栈-CSDN博客
1.3 堆和栈的区别
1.3.1 分配方式
堆都是动态分配的,由程序员手动控制,堆的分配可以在任意时刻进行,不需要遵循特定的顺序。
栈有 2 种分配方式:静态分配和动态分配。
静态分配是由操作系统完成的,由编译器自动控制,栈的分配在编译时确定,无法在运行时改变,比如局部变量的分配。
动态分配由alloca()函数分配,但是栈的动态分配和堆是不同的,它的动态分配是由操作系统进行释放,无需我们手工实现。
栈还用于中断时的保存现场和恢复现场。
简单来说,
堆的分配和释放由程序员控制,分配后可变;
栈的分配和释放由编译器控制,分配后不可变。
1.3.2 分配效率
堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。
栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆的分配效率较低,需要在运行时动态分配和释放。
栈的分配效率较高,由编译器在编译时确定分配大小。
1.3.3 生长方向
堆的生长方向向上,内存地址由低到高;
对于堆需要注意:后申请的内存空间并不一定在先申请的内存空间的后面,原因是先申请的内存空间一旦被释放,后申请的内存空间则会利用先前被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系。
栈的生长方向向下,内存地址由高到低。
1.3.4 空间管理
堆的空间管理由程序员手动控制,需要注意内存泄漏和内存碎片的问题。
栈的空间管理由编译器自动控制,无效程序员关注。
1.3.5 存放内容
堆:
任务控制块(TCB):TaskHandle_t 指向的结构体,包含任务状态、优先级、栈指针等信息。
任务栈空间(动态分配时):通过 xTaskCreate() 创建任务时,从堆中分配栈内存。
队列(Queue):xQueueCreate() 创建的队列数据结构和存储区域。
信号量/互斥量:xSemaphoreCreateBinary()、xSemaphoreCreateMutex() 等创建的同步对象。
定时器(Timer):xTimerCreate() 创建的软件定时器结构。
动态分配的缓冲区:用户通过 pvPortMalloc() 手动申请的内存块。
栈:
局部变量:函数内定义的变量(如 int temp)。
函数调用链:返回地址、函数参数、寄存器保存值(中断或任务切换时)。
中断现场:发生中断时,CPU 自动压栈的寄存器状态(如 R0-R12, LR, PC)。
表达式计算:算术运算的中间结果。
拓展:
嵌入式面试八股文(十一)·FreeRTOS相关题目_freertos习题-CSDN博客
千题千解·嵌入式工程师八股文详解_时光の尘的博客-CSDN博客