如何统计RTOS用了多少RAM资源

好久没写我和妹子的故事了,甚至都有人问怎么不更新了,最近怎样了。

主要原因是“懒”。

至于近况,我还是原来的我,而妹子是是聪明的妹子,最近进步不少,很多技术问题都已经搞定了,问我的比较少。

而今天,妹子突然问了我一个RTOS资源的问题,细细了解了下,原来她被人“将军”了,也就是她被人怼了。
岂有此理,欺负妹子不就是欺负我吗!

就在上周,她去参加了一个项目的启动会议,谈到MCU选型和内存预估的问题,妹子就将以往的一个类似的项目的数据搬过来用,偏偏遇到了个较真的项目经理。

项目经理问为什么要用这么多内存,能不能再少30%,好换个低一档资源的MCU(主要是为了节省成本)。

妹子说,这项目跟以往的比较接近,这些都是经验值,没办法再优化了,等等。

项目经理,对这话不满意,觉得小丫头忽悠他,让她拿出评估数据。

妹子一时半会弄不出来,就说下周给。

会后,妹子觉得委屈了,然而也觉得自己做得不够,此时也明白,要镇住这项目经理,是要靠数据的。

于是她,左思右想,还翻了一堆代码,也不好做这个数据。但是她最终看到了不是很明确的一大块数据使用,有30多KB,想了好久,不知怎么评估。

于是,今天早上,她就找我了。

看着她委屈楚楚可怜的样子,我微微笑了下,说,“放心,哥今天帮你搞定”。
“授人以鱼不如授人以渔”,我决定今天手把手教会妹子分析RTOS内存消耗情况。

首先,查看了下,FreeRTOS config.h文件里面的配置,确实用了32KB。

#define configTOTAL_HEAP_SIZE  ( ( size_t ) ( 32 * 1024U ) )

为什么用了这么多呢?

搜索这个“configTOTAL_HEAP_SIZE”,能找到

PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

那么怎么统计ucHeap的使用情况呢?

接着,我们找到这个pvPortMalloc函数

void * pvPortMalloc( size_t xWantedSize )
{
// ... ...
        traceMALLOC( pvReturn, xWantedSize );
    }
// ... ...
}

里面有个traceMALLOC,通过这个函数,我们不是可以统计每次申请的内存了?

而traceMALLOC是啥呢?

在FreeRTOS.h

#ifndef traceMALLOC
    #define traceMALLOC( pvAddress, uiSize )
#endif

原来,这是要自定义的……

一顿操作猛如虎,妹子觉得我威武。

#define traceMALLOC traceMALLOCFun

// ... ...

typedef struct
{
    void* ret;
    size_t size;
}AllockInfoType;

AllockInfoType FreeRTOSAllocInfo[1024] = {{.ret=(void*)0}};
int AllocCounter = 0;
void traceMALLOCFun(void* ret, size_t size)
{
    FreeRTOSAllocInfo[AllocCounter].ret = ret;
    FreeRTOSAllocInfo[AllocCounter].size = size;
    AllocCounter++;
}

然后,我们统计FreeRTOSAllocInfo里面的数据就可以了

通过查看内存,得到的数据是

204088B0 00 00 00 00 00 00 00 00 C0 08 40 20 08 04 00 00
204088C0 C8 0C 40 20 78 00 00 00 40 0D 40 20 08 04 00 00
204088D0 48 11 40 20 78 00 00 00 C0 11 40 20 A8 00 00 00
204088E0 68 12 40 20 08 08 00 00 70 1A 40 20 78 00 00 00
204088F0 E8 1A 40 20 D8 00 00 00 C0 1B 40 20 E8 02 00 00
20408900 A8 1E 40 20 58 00 00 00 00 1F 40 20 58 00 00 00
20408910 58 1F 40 20 58 00 00 00 B0 1F 40 20 58 00 00 00
20408920 08 20 40 20 58 00 00 00 60 20 40 20 D8 00 00 00
20408930 38 21 40 20 58 00 00 00 90 21 40 20 58 00 00 00
20408940 E8 21 40 20 08 10 00 00 F0 31 40 20 78 00 00 00
20408950 68 32 40 20 58 00 00 00 C0 32 40 20 58 00 00 00
20408960 18 33 40 20 08 08 00 00 20 3B 40 20 78 00 00 00
20408970 98 3B 40 20 08 10 00 00 A0 4B 40 20 78 00 00 00
20408980 18 4C 40 20 08 10 00 00 20 5C 40 20 78 00 00 00
20408990 98 5C 40 20 58 00 00 00 F0 5C 40 20 08 04 00 00
204089A0 F8 60 40 20 78 00 00 00 70 61 40 20 08 20 00 00
204089B0 78 81 40 20 78 00 00 00 F0 81 40 20 58 00 00 00
204089C0 48 82 40 20 D8 00 00 00 00 00 00 00 00 00 00 00
204089D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
204089E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
204089F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20408A00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20408A10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

妹子在旁边低声说,“这还真是原生数据啊,咋看呢?”

“淡定,慢慢来……”我一边说一边操作。

于是,我把左边的地址信息删了,然后通过正则表达式格式化得到

Address     Size
204008C0    00000408
20400CC8    00000078
20400D40    00000408
20401148    00000078
204011C0    000000A8
20401268    00000808
20401A70    00000078
20401AE8    000000D8
20401BC0    000002E8
20401EA8    00000058
20401F00    00000058
20401F58    00000058
20401FB0    00000058
20402008    00000058
20402060    000000D8
20402138    00000058
20402190    00000058
204021E8    00001008
204031F0    00000078
20403268    00000058
204032C0    00000058
20403318    00000808
20403B20    00000078
20403B98    00001008
20404BA0    00000078
20404C18    00001008
20405C20    00000078
20405C98    00000058
20405CF0    00000408
204060F8    00000078
20406170    00002008
20408178    00000078
204081F0    00000058
20408248    000000D8
00000000    00000000
00000000    00000000
00000000    00000000
00000000    00000000

妹子顿时惊叹,原来还可以这么做。

我顺势说,“有空,我教你正则表达式啊”。

她笑了,我也笑了。

再通过Excel生成个图

“哇”,在看到图片后,她信心满满的,觉得胸有成竹可以回怼项目经理了。

这就是RTOS运行一会后的Heap申请情况。

经过以上的统计,这个Heap已经被使用超过90%了。
妹子是很好学的,她问:

“但是,怎么知道那些资源各占多少呢?”

“这有点麻烦琐碎……”

我找了个task试试教给她看

仿真进入create task函数

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
{
        TCB_t * pxNewTCB;
        BaseType_t xReturn;
// ...
                /* Allocate space for the stack used by the task being created. */
                pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 

                if( pxStack != NULL )
                {
                    /* Allocate space for the TCB. */
                    pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

这里有两个信息要注意:

  1. xTaskCreate传进来的usStackDepth并非是task stack的实际大小,这里还要乘以sizeof( StackType_t ),一般是4倍

  2. 还要为TCB分配空间,即pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

这样,我们就知道一个task需要占用多少资源了。

另外,趁热打铁,再找个mailbox的看看,其实际调用的是xQueueGenericCreate

QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
                                       const UBaseType_t uxItemSize,
                                       const uint8_t ucQueueType )
{
        Queue_t * pxNewQueue;
        size_t xQueueSizeInBytes;
        uint8_t * pucQueueStorage;
// ...
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

方法以此类推,等等
又一顿操作后,我们算出了很多数据,妹子雪亮的眼睛发现:
“为什么测试得出的数据跟配置的数据还差一截?”
她并非怀疑我的操作,而是有些疑惑。
“实际上,FreeRTOS还默认创建了IDLE Task和Tmr Svc Task。”

如vTaskStartScheduler调用了xTimerCreateTimerTask

void vTaskStartScheduler( void )
{
// ...
    #if ( configUSE_TIMERS == 1 )
        {
            if( xReturn == pdPASS )
            {
                xReturn = xTimerCreateTimerTask();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    #endif /* configUSE_TIMERS */

而xTimerCreateTimerTask如下

BaseType_t xTimerCreateTimerTask( void )
{
        BaseType_t xReturn = pdFAIL;
// ... 
                {
                    xReturn = xTaskCreate( prvTimerTask,
                                           configTIMER_SERVICE_TASK_NAME,
                                           configTIMER_TASK_STACK_DEPTH,
                                           NULL,
                                           ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
                                           &xTimerTaskHandle );
                }

以上是原生态的分析RTOS的资源分配分析方式。
妹子对我的技术佩服又进了一步……


(以下内容,我是没有给妹子讲的,免得她说搞了半天,原来是有现成工具可以做的。这意义是不一样的啊,不是么?)


其实,目前市面上很多IDE已经支持Free RTOS的资源分配情况,如

另外,还有个问题,如何统计Task的Stack使用情况,如何定义合理的Task Stack,老是说这是“经验值”,没有数据支持,也会被怼的。

实际上FreeRTOS提供了这样的函数uxTaskGetStackHighWaterMark,来检测Stack的使用情况。

很多IDE也支持在debug情况下获得Task的运行情况

关键的问题点是,如何检测好Stack使用的worst case。

引申出另一个问题:如何测试MCU的CPU占用率?

这个问题,我觉得妹子还是会问我的,因为这个项目的客户有关注这个问题点,迟早要做报告……


好了,以上内容,妹子说她都掌握了,剩下的她可以自己做了。
我看了下手机上的时间,快下班了。
手机上还明显提示,今天是“情人节”……

静默几秒钟……

我知道她常常加班,应该是没男朋友的,我厚着脸皮问了一句妹子,“要下班了,你今晚要回去过节吧?”

“过啥节!”她笑了下,说:“师兄你要下班了吧”。
“呃……哦,呵呵,想下班了呢。”
“剩下的,我自己可以搞定了,谢谢师兄!”

静默几秒钟……

“要……要不……我们……一起,呃,一起整完它吧,今晚就不要加班了……”
脑子有点不好使了,原是想,是不是叫她一起去吃饭来着,突然想着,这个日子有点特殊,这不是太明显了……后来想着叫她不要加班了,但是我又蹦出了个“一起”……

差点就尴尬了……

关注公众号“嵌入式软件实战派”,获得更多我和妹子的故事。

 

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 基于8051的rtos是一种实时操作系统,它专门用来管理主要基于8051微控制器的嵌入式系统。这种操作系统比起普通操作系统更加适合嵌入式系统的应用,因为它可以对系统资源进行精细的管理和调度,从而实现系统的可靠性和高效性。 基于8051的rtos中,主要包含了一些常用的任务,如管理任务、通信任务、数据处理任务、控制任务等等。这些任务可以根据实际需要通过系统调用来管理和控制,而不必担心竞争条件的出现,这样就可以保证任务的顺序执行和资源使用的正确性。 另外,基于8051的rtos还可以提供一些重要的特性,如优先级调度、时间延迟、事件信号处理等等。这些功能都可以针对不同的应用需求进行定制化和配置,从而实现特殊功能的实时响应和高效处理。 总之,基于8051的rtos可以为嵌入式系统提供高效的任务调度、丰富的系统功能、灵活的应用定制等优势,是嵌入式系统开发的重要工具之一。 ### 回答2: 基于8051的RTOS是一种实时操作系统,该操作系统适用于嵌入式系统,尤其是利用8051处理器的系统。该RTOS允许多个任务同时运行,并能够确保它们按时完成。 基于8051的RTOS提供了很多功能,包括任务管理,内存管理,时间管理,中断管理等等。其中,任务管理可以让多个任务同时运行,并确保它们按时完成。内存管理可以管理系统内存的分配和释放,确保系统使用内存的效率。时间管理则负责管理系统的时钟,以确保所有任务按照预期的时间执行。中断管理可以处理输入和输出设备的中断。 基于8051的RTOS实现原理是,把整个程序分成多个任务并按照优先级和执行时间安排它们的执行。RTOS会利用定时器,中断和任务切换等方式来协调多个任务的执行,从而确保它们能够按时完成。 总的来说,基于8051的RTOS是一种实时操作系统,它可以使嵌入式系统中的多个任务同时运行,从而提高整个系统的效率和稳定性。同时,它也具备任务管理、内存管理、时间管理、中断管理等一系列功能,为嵌入式系统的开发和调试提供了很大的帮助。 ### 回答3: 基于8051的RTOS(Real-time Operating System,实时操作系统),是一种适用于小型嵌入式设备的实时操作系统。它基于底层的8051芯片,具有实时性强、可靠性高、响应速度快等特点,适用于需要快速响应并具有实时性要求的场合。 通常情况下,基于8051的RTOS可分为两大类,即裸机式实时操作系统和操作系统/编译器内置的实时操作系统。前者是指只有一个RTOS内核,而没有其他的元素,开发者需要自己根据实际需求编写系统代码,因此开发难度较高。后者则是指在编译器或操作系统中已经内置了RTOS,需要开发者按照其提供的API进行开发,相对来说开发难度会更低一些。 基于8051的RTOS具有许多优点,比如快速的中断响应、高度的灵活性、简化的应用程序设计和维护、更少的RAM和ROM使用量等。它还具有广泛的应用领域,比如自动化控制、家电控制、交通工具、医疗设备、工业自动化控制等。 总之,基于8051的RTOS在嵌入式设备中发挥着不可替代的作用,其应用广泛,可通过不同的编译器和库构建,帮助开发者更高效、更快速地完成应用程序的设计和实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值