FreeRTOS中的堆栈计算
1.栈的重要性其实不管是普通的程序还是Freertos程序,分配的栈的大小是很重要的,要不然带不动程序,就容易造成程序的崩溃。函数调用时的现场保护和返回地址,函数的形参,进入中断函数前和中断嵌套等都需要栈空间。2.检测堆大小xPortGetFreeHeapSize()可以获取调用时堆中空闲内存的大小,以字节为单位。使用它可以优化堆的大小。需要注意,当使用heap_3时是不能调用这个函数的。xPor
1.栈的重要性
其实不管是普通的程序还是Freertos程序,分配的栈的大小是很重要的,要不然带不动程序,就容易造成程序的崩溃。
函数调用时的现场保护和返回地址,函数的形参,进入中断函数前和中断嵌套等都需要栈空间。
2.检测堆大小
xPortGetFreeHeapSize
()
可以获取调用时堆中空闲内存的大小,以字节为单位。使用它可以优化堆的大小。需要注意,当使用heap_3时是不能调用这个函数的。
xPortGetMinimumEverFreeHeapSize
()此函数返回FreeRTOS应用程序开始运行之后,曾经存在的最小的未被分配的存储空间的字节数。
需要注意xPortGetMinimumEverFreeHeapSize()只在使用heap_4或者heap_5时生效。
printf(“xPortGetFreeHeapSize = %d\r\n”, xPortGetFreeHeapSize());
printf(“xPortGetMinimumEverFreeHeapSize = %d\r\n”,xPortGetMinimumEverFreeHeapSize());
例如:两个函数的返回值打印出来,比如分别为2200和2000。2200代表目前还有2200可以用,2000:代表程序的堆分配了最多后还剩下2000bytes可以用,我们就可以适当的减小这个数值,但是又不能全部减掉,适当的留一些,以备不时之需。而我们一开始分配时,也要先从大往小的方向适当调整堆的大小。
3.检测栈大小
uxTaskGetStackHighWaterMark()
printf(" 最小的栈空间大小: %d \r\n",
(int32_t)uxTaskGetStackHighWaterMark(NULL));
可以得出该任务自启动起来最小剩余栈空间大小。然后我们就可以计算出最大使用的大小,一般可以再乘以1.5-2倍左右作为最终分配的值。注意:返回的以字为单位,真实的bytes需要乘以4。
4.程序里的堆栈大小
这里的栈空间又叫做系统栈空间:中断函数和中断嵌套才用的到这些栈。
任务栈使用这里分配的栈空间。
5.计算堆栈大小
对于Cortex-M3内核和未使用FPU(浮点运算单元)功能的Cortex-M4内核在发生中断时需要将16个通用寄存器全部入栈,每个寄存器占用4个字节,也就是16*4 = 64字节的空间。 可能发生几次中断嵌套就是要64乘以几即可。当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
(注:任务执行的过程中发生中断的话,有8个寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余寄存器入栈以及发生中断嵌套都是用的系统栈)
对于具有FPU(浮点运算单元)功能的Cortex-M4内核,如果在任务中进行了浮点运算,那么在发生中断的时候除了16个通用寄存器需要入栈,还有34个浮点寄存器也是要入栈的,也就是(16+34)*4 = 200字节的空间。当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
(注:任务执行的过程中发送中断的话,有8个通用寄存器和18个浮点寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系统栈)。
6.堆栈溢出检测
设置堆栈溢出检测,每个任务都有一个任务堆栈,如果使用函数xTaskCreate()创建一个任务的话那么这个任务的堆栈是自动从
FreeRTOS的堆(ucHeap)中分配的,堆栈的大小是由函数xTaskCreate()的参数usStackDepth
来决定的。如果使用函数xTaskCreateStatic()创建任务的话任务堆栈是由用户设置的,参数pxStackBuffer为任务堆栈,一般是一个数组。
堆栈溢出是导致应用程序不稳定的主要因素,FreeRTOS
提供了两种可选的机制来帮助检测和调试堆栈溢出,不管使用哪种机制都要设置宏configCHECK_FOR_STACK_OVERFLOW。如果使能了堆栈检测功能的话,即宏configCHECK_FOR_STACK_OVERFLOW不为0,那么用户必须提供一个钩子函数(回调函数),当内核检测到堆栈溢出以后就会调用这个钩子函数,此钩子函数原型如下:
void vApplicationStackOverflowHook( TaskHandle_t xTask,
char *pcTaskName );
参数xTask是任务句柄,pcTaskName是任务名字,要注意的是堆栈溢出太严重的话可能会损毁这两个参数,如果发生这种情况的话可以直接查看变量 pxCurrentTCB来确定哪个任务发生了堆栈溢出。
有些处理器可能在堆栈溢出的时候生成一个fault中断来提示这种错误,另外,堆栈溢出检测会增加上下文切换的开销,建议在调试的时候使用。
6.1 堆栈溢出检测方法1
configCHECK_FOR_STACK_OVERFLOW==1,使用堆栈溢出检测方法1。
上下文切换的时候需要保存现场,现场是保存在堆栈中的,这个时候任务堆栈使用率很可能达到最大值,方法一就是不断的检测任务堆栈指针是否指向有效空间,如果指向了无效空间的话就会调用钩子函数。方法一的优点就是快!但是缺点就是不能检测所有的堆栈溢出。
6.2 堆栈溢出检测方法2
configCHECK_FOR_STACK_OVERFLOW==2,使用堆栈溢出检测方法2。
使用方法二的话在创建任务的时候会向任务堆栈填充一个已知的标记值,方法二会一直检测堆栈后面的几个bytes(标记值)是否被改写,如果被改写的话就会调用堆栈溢出钩子函数,方法二也会使用方法一中的机制!方法二比方法一要慢一些,但是对用户而言还是很快的!方法二能检测到几乎所有的堆栈溢出,但是也有一些情况检测不到,比如溢出值和标记值相同的时候。
总结
欢迎指正
学习使用
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)