FreeRTOS 任务栈大小确定及其溢出检测
以下转载自安富莱电子: http://forum.armfly.com/forum.php
FreeRTOS 的任务栈设置
不管是裸机编程还是 RTOS 编程,栈的分配大小都非常重要。 局部变量,函数调用时的现场保护和返
回地址,函数的形参,进入中断函数前和中断嵌套等都需要栈空间,栈空间定义小了会造成系统崩溃。
裸机的情况下,用户可以在这里配置栈大小:
为什么是堆中的?因为我们采用的就是动态创建任务的方式。如果静态创建,就和我们自己开辟的空间有关,通常静态创建任务用数组作为容器,但是通常静态创建的方式我们都不使用。
FreeRTOS 的系统栈设置
上面跟大家讲解了什么是任务栈,这里的系统栈又是什么呢?裸机的情况下,凡是用到栈空间的地方
都是在这里配置的栈空间:
在 RTOS 下,上面两个截图中设置的栈大小有了一个新的名字叫系统栈空间,而任务栈是不使用这里的空间的。 任务栈不使用这里的栈空间,哪里使用这里的栈空间呢?答案就在中断函数和中断嵌套。
由于 Cortex-M3 和 M4 内核具有双堆栈指针,MSP 主堆栈指针和 PSP 进程堆栈指针,或者叫 PSP
任务堆栈指针也是可以的。在 FreeRTOS 操作系统中,主堆栈指针 MSP 是给系统栈空间使用的,进
程堆栈指针 PSP 是给任务栈使用的。 也就是说,在 FreeRTOS 任务中,所有栈空间的使用都是通过
PSP 指针进行指向的。 一旦进入了中断函数以及可能发生的中断嵌套都是用的 MSP 指针。这个知识
点要记住它,当前可以不知道这是为什么,但是一定要记住。
实际应用中系统栈空间分配多大,主要是看可能发生的中断嵌套层数,下面我们就按照最坏执行情况
进行考虑,所有的寄存器都需要入栈,此时分为两种情况:
64 字节
对于 Cortex-M3 内核和未使用 FPU(浮点运算单元)功能的 Cortex-M4 内核在发生中断时需
要将 16 个通用寄存器全部入栈,每个寄存器占用 4 个字节,也就是 16*4 = 64 字节的空间。
可能发生几次中断嵌套就是要 64 乘以几即可。 当然,这种是最坏执行情况,也就是所有的寄存
器都入栈。
(注:任务执行的过程中发生中断的话,有 8 个寄存器是自动入栈的,这个栈是任务栈,进入中
断以后其余寄存器入栈以及发生中断嵌套都是用的系统栈)
200 字节
对于具有 FPU(浮点运算单元)功能的 Cortex-M4 内核,如果在任务中进行了浮点运算,那么
在发生中断的时候除了 16 个通用寄存器需要入栈,还有 34 个浮点寄存器也是要入栈的,也就是
(16+34)*4 = 200 字节的空间。当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
(注:任务执行的过程中发送中断的话,有 8 个通用寄存器和 18 个浮点寄存器是自动入栈的,
这个栈是任务栈,进入中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系
统栈)
FreeRTOS 的任务状态
FreeRTOS 的运行支持以下四种状态:
Running—运行态
当任务处于实际运行状态被称之为运行态,即 CPU 的使用权被这个任务占用。
Ready—就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起) ,但是当前没有运行的任务,因为同优先
级或更高优先级的任务正在运行。
Blocked—阻塞态
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟函数也
会处于阻塞态。
Suspended—挂起态
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只
有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。
使用如下函数即可启动 FreeRTOS:
vTaskStartScheduler();
函数原型:
void vTaskStartScheduler( void );
函数描述:
函数 vTaskStartScheduler 用于启动 FreeRTOS 调度器,即启动 FreeRTOS 的多任务执行。
使用这个函数要注意以下几个问题:
1. 空闲任务和可选的定时器任务是在调用这个函数后自动创建的。
2. 正常情况下这个函数是不会返回的,运行到这里极有可能是用于定时器任务或者空闲任务的 heap 空
间不足造成创建失败,此时需要加大 FreeRTOSConfig.h 文件中定义的 heap 大小:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
FreeRTOS 的任务恢复
使用如下函数可以实现 FreeRTOS 的任务恢复:
xTaskResume()
使用如下函数可以实现 FreeRTOS 的任务恢复(中断方式):
xTaskResumeFromISR()
任务栈大小的确定
在基于 RTOS 的应用设计中,每个任务都需要自己的栈空间,应用不同,每个任务需要的栈大小也是
不同的。 将如下的几个选项简单的累加就可以得到一个粗略的栈大小:
函数的嵌套调用,针对每一级函数用到栈空间的有如下四项:
函数局部变量。
函数形参,一般情况下函数的形参是直接使用的 CPU 寄存器,不需要使用栈空间,但是这个函
数中如果还嵌套了一个函数的话,这个存储了函数形参的 CPU 寄存器内容是要入栈的。 所以建
议大家也把这部分算在栈大小中。
函数返回地址,针对 M3 和 M4 内核的 MCU,一般函数的返回地址是专门保存到 LR(Link
Register)寄存器里面的,如果这个函数里面还调用了一个函数的话,这个存储了函数返回地址
的 LR 寄存器内容是要入栈的。 所以建议大家也把这部分算在栈大小中。
函数内部的状态保存操作也需要额外的栈空间。
任务切换,任务切换时所有的寄存器都需要入栈,对于带 FPU 浮点处理单元的 M4 内核 MCU 来说,
FPU 寄存器也是需要入栈的。
针对 M3 内核和 M4 内核的 MCU 来说,在任务执行过程中,如果发生中断:
M3 内核的 MCU 有 8 个寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余寄存器入栈
以及发生中断嵌套都是用的系统栈。
M4 内核的 MCU 有 8 个通用寄存器和 18 个浮点寄存器是自动入栈的,这个栈是任务栈,进入
中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系统栈。
进入中断以后使用的局部变量以及可能发生的中断嵌套都是用的系统栈,这点要注意。
实际应用中将这些都加起来是一件非常麻烦的工作,上面这些栈空间加起来的总和只是栈的最小需求,
实际分配的栈大小可以在最小栈需求的基础上乘以一个安全系数,一般取 1.5-2。上面的计算是我们用户
可以确定的栈大小,项目应用中还存在无法确定的栈大小,比如调用printf函数就很难确定实际的栈消耗。
又比如通过函数指针实现函数的间接调用,因为函数指针不是固定的指向一个函数进行调用,而是根据不
同的程序设计可以指向不同的函数,使得栈大小的计算变得比较麻烦。
另外还要注意一点,建议不要编写递归代码,因为我们不知道递归的层数,栈的大小也是不好确定的。
一般来说,用户可以事先给任务分配一个大的栈空间,然后通过第 8 章介绍的调试方法打印任务栈的
使用情况,运行一段时间就会有个大概的范围了。这种方法比较简单且实用些。
函数栈大小确定
函数的栈大小计算起来是比较麻烦的, 那么有没有简单的办法来计算呢? 有的,一般 IDE 开发环境都
有这样的功能,比如 MDK 会生成一个 htm 文件,通过这个文件用户可以知道每个被调用函数的最大栈
需求以及各个函数之间的调用关系。但是 MDK 无法确定通过函数指针实现函数调用时的栈需求。另外,
发生中断或中断嵌套时的现场保护需要的栈空间也不会统计。 关于 MDK 生成的 map 和 htm 文件的使用,安富莱电子有出过一期视频教程,可以在这里查看:
http://bbs.armfly.com/read.php?tid=15408
什么是栈溢出
前面为大家讲解了如何确定任务栈的大小,那什么又是栈溢出呢?简单的说就是用户分配的栈空间不
够用了,溢出了。 下面我们举一个简单的实例,栈生长方向从高地址向低地址生长(M4 和 M3 是这种方
式)。
FreeRTOS 的栈溢出检测机制
FreeRTOS 提供了两种栈溢出检测机制,这两种检测都是在任务切换时才会进行:
方法一
在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函
数。
void vApplicationStackOverflowHook( TaskHandle_t xTask,
signed char *pcTaskName );
用户可以在钩子函数里面做一些处理。这种方法不能保证所有的栈溢出都能检测到。比如任务在执行
的过程中出现过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测
不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有
用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也
是检测不到的。
使用方法一需要用户在 FreeRTOSConfig.h 文件中配置如下宏定义:
#define configCHECK_FOR_STACK_OVERFLOW 1
方法二
任务创建的时候将任务栈所有数据初始化为 0xa5,任务切换时进行任务栈检测的时候会检测末
尾的 16 个字节是否都是 0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速
度稍慢些,但是这样就有效地避免了方法一里面的部分情况。 不过依然不能保证所有的栈溢出都能检
测到,比如任务栈末尾的 16 个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是
检测不到的。 另外任务栈溢出后,任务栈末尾的 16 个字节没有修改,但是溢出部分的栈区数据被修
改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进
入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
使用方法二需要用户在 FreeRTOSConfig.h 文件中配置如下宏定义:
#define configCHECK_FOR_STACK_OVERFLOW 2
钩子函数
钩子函数的主要作用就是对原有函数的功能进行扩展,用户可以根据自己的需要往里面添加相关的测
试代码, 大家可以在 FreeRTOS 工程中检索这个钩子函数 vApplicationStackOverflowHook 所在的位
置。
除了 FreeRTOS 提供的这两种栈溢出检测机制,还有其它的栈溢出检测机制,大家可以在 Mircrium 官
方发布的如下这个博文中学习:
https://www.micrium.com/detecting-stack-overflows-part-2-of-2/
对于这种溢出检测,我测试了几种情况,确实不容易检测到,多半进入硬件错误中断,所以以后要是遇到程序硬件中断死循环,清注意检测堆栈大小。不确定的时候,先使用printf调试打印任务栈的剩余,选择一个合理安全的值作为堆栈设置,这确实是个难以简单计算的东西。
FreeRTOS 任务栈大小确定及其溢出检测的更多相关文章
- 【RTOS】FreeRTOS中的任务堆栈溢出检测机制
目录 前言 任务堆栈 堆栈溢出 任务堆栈溢出检测机制 API 两种堆栈溢出检测方式 堆栈溢出钩子函数 内核何时检测任务堆栈溢出 任务堆栈溢出检测存在的局限性 前言 注意:本笔记发布时可能忘记补充查看d ...
- 浅析软件测试人员如何对JVM进行内存溢出检测
一.什么是JVM,检测JVM的意义 JVM是java virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各 ...
- hadoop java VM 参数设置 默认native栈大小设置
问题总结: 程序栈太小,64位机器的栈大小默认比32位的大,将程序从64放到32中执行则报错,需要修改初始堆栈大小 (.so库中提供两个函数接口,一个里面使用的是尺寸较大的图像,另一个处理的图像很小, ...
- 程序员---C语言细节7(增加两个整数溢出检测)
主要内容:增加两个整数溢出检测 #include <stdio.h> #include <limits.h> int main(int argc, char *argv[]) ...
- Linux 默认线程栈大小 调优
Linux 线程栈介绍 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区.里面的变量通常是局部变量.函数参数等:和堆相比,栈通常很小. Linux 查询线程栈 1.查看默认的 ...
- [转帖] Windows 与linux的栈大小问题
一般来说,我们所用的内存有栈和堆之分,其它的我们很少控制,栈的速度快,但是空间小.不灵活:而堆的空间几乎可以满足任何要求.灵活,但是相对的速度要慢了很多,并且在VC中堆是人为控制的,new了就要del ...
- 自己写个 Drools 文件语法检查工具——栈的应用之编译器检测语法错误
一.背景 当前自己开发的 Android 项目是一个智能推荐系统,用到 drools 规则引擎,于我来说是一个新知识点,以前都没听说过的东东,不过用起来也不算太难,经过一段时间学习,基本掌握.关于 d ...
- linux多线程默认栈大小和最大线程数
linux的线程栈大小可以使用ulimit -s查看,对于ubunt 2.6的内核线程栈的默认大小为8M,如下: shine@shine-bupt:~/Program/C$ ulimit -s 819 ...
- 转载:/etc/security/limits.conf 控制文件描述符,进程数,栈大小
原文地址:http://ilikedo.iteye.com/blog/1554822 linux下安装Oracle 一般都会修改/etc/security/limits.conf这个文件,但是这里面的 ...
随机推荐
- java RandomAccessFile类(随机访问文件)
该类可以实现对同一个文件的读写操作,与其他IO流不同的是可以指定读写指针的脚标(seek),有跳过指定个数字节(skipBytes)操作. 另外该类也可用于断点续传. 简单示例如下: import j ...
- JavaScript-各种插件
上传插件: 首推:uploadify http://www.admin10000.com/document/2980.html 滚动条插件: http://www.admin10000.com/doc ...
- aop:declare-parents注解
http://www.blogjava.net/jackfrued/archive/2010/02/27/314060.html <aop:aspect> <aop:declare- ...
- jQuery瀑布流无限拖三大利器:masonry+imagesloaded+infinitescroll
瀑布流已经是几乎过时的技术了,不过对于很多想要快速实现它的朋友而言,却绝非易事,因为即使我们已经有很多现成的代码,却发现在自己的开发环境中无法快速得到自己想要的结果.就像我们现在要介绍的三大利器(ma ...
- Linux时间子系统(十四) tick broadcast framework
一.前言 在内核中,有cpuidle framework可以控制cpu的节电:当没有进程调度到该cpu上执行的时候,swapper进程粉墨登场,将该cpu会被推入到idle状态.当然CPU的idle状 ...
- python标准库介绍——24 traceback 模块详解
==traceback 模块== [Example 2-18 #eg-2-18] 展示了 ``traceback`` 模块允许你在程序里打印异常的跟踪返回 (Traceback)信息, 类似未捕获异常 ...
- 不能与abstruct共同修饰方法的关键字
一 抽象类:动态方法至少有一个是抽象方法. 其中abstruct关键字修饰的方法不能与哪些关键字共同修饰? 1private 因为private修饰的方法在子类中是隐藏的.不可见的.而abstruct ...
- cocos2dx 3.3 场景切出时RenderTexture crash
在cocos2dx 3.3中下面myScene在切出时会存在概率性崩溃(代码作了最大程度简化,仅为说明问题): class CmyLayer:public Layer{ public: CmyLaye ...
- windows live writer 2012 0x80070643
折腾了两天,windows live writer 安装不成功,最后放弃了,发现一个叫做菊子曰的软件,但是免费版本的,发图片有限制,感觉非常不爽.windows live writer报错如下 ...
- Java总结篇系列:Java多线程(四)
ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地 ...