当前位置:首页 > 公众号精选 > 嵌入式软件实战派
[导读]这次我成功将妹子约到了公司附近的咖啡馆,继续探讨RTOS的heap的技术特点。当我把准备好的数据和动图展示在她面前,她立马激动起来了。

这次我成功将妹子约到了公司附近的咖啡馆,继续探讨RTOS的heap的技术特点。当我把准备好的数据和动图展示在她面前,她立马激动起来了。仿佛我递出来的是一束花、钻戒,她惊讶不已,除了脸上少了几分娇羞……事情上这样的,我得好好重头讲一讲。1. 妹子的问题妹子好久没有问我问题了,想着应该是可以自己独立干项目了吧。这也是好事,毕竟是我亲自带出来的徒弟。但我还是怀念她经常问我问题的日子……
有一天傍晚快下班的时候,我问她,项目上的事自己能hold得住吗?有问题随时可以找我哦。
她想了想,欲言又止。
还没等她说话,我接着说,是遇到什么问题了吗?
“其实是这样的,我是遇到了OS上的一些疑惑问题,但也不是很要紧。看你那么忙,不好意思打扰你……”“哦……没事啊,什么问题呢?”她说,自从上次我跟她分析了OS占用内存的问题(见妹子告诉我她被欺负了)后,她就对OS的内存分配有不少疑惑,很想将它搞个一清二楚。这不,她现在的问题是,FreeRTOS里的Heap1~Heap5有什么区别?
心想:额……这次完犊子了,我还真没怎么研究过这几个Heap的区别。但是我不能在她面前说不行啊。
突然,心生一计,若有其事淡定地跟她说,这是个好问题,得好好探讨下。然后我顺手拿出手机看了看时间,问她,明天晚上有空么?
“有。”她回答很干脆。
这次,我得换个方式跟她讲解这个Heap,不能像以前那样了……
2. 熬夜研究heap于是,我背了笔记本回家,开始钻研这个heap的用法,得用丰富的知识和强有力的技能征服妹子。
首先,我将这几个heap的c文件移植到PC运行环境,用自动化手段分析其使用情况。我写了个main.c文件,分别与heap_1.c~heap_5.c进行编译。
主要思路是,向heap申请并释放内存,看看其地址占用空间,就一目了然了。
 addr1 = alloc_x(1); addr2 = alloc_x(2); addr4 = alloc_x(4); addr8 = alloc_x(8); addr16 = alloc_x(16); addr32 = alloc_x(32);  free_x(addr1); addr1 = alloc_x(1); free_x(addr4); addr2 = alloc_x(2); free_x(addr8); addr4 = alloc_x(4); addr4 = alloc_x(4); free_x(addr16); addr1 = alloc_x(1); addr2 = alloc_x(2); addr4 = alloc_x(4); addr8 = alloc_x(8); addr16 = alloc_x(16); addr1 = alloc_x(1); free_x(addr2); free_x(addr4); free_x(addr8); free_x(addr16); addr6 = alloc_x(6); addr10 = alloc_x(10);
这里面的alloc_xfree_x分别调用了pvPortMallocvPortFree,并打印一些信息。
void* alloc_x(size_t size){ void* addr = pvPortMalloc(size); size_t res = xPortGetFreeHeapSize(); printf("Alloc : addr=0x%p, size:0x%02X, remain:0x%04X\r\n", addr, size, res); return addr;} void free_x(void* addr){ vPortFree( addr); size_t res = xPortGetFreeHeapSize(); printf("Free  : addr=0x%p, remain:0x%04X\r\n", addr, res);}
总以为很顺利,谁知道,这heap依赖的头文件一层套一层……一狠心,大刀阔斧屏蔽了头文件里面的内容,手动添加heap需要的依赖项。
好不容易,编译通过了,在搞heap_1.c这个的时候,在运行到vPortFree,就挂了!查了下代码,发现heap_1原来是不允许free内存的。
void vPortFree( void * pv ){ /* Memory cannot be freed using this scheme.  See heap_2.c, heap_3.c and * heap_4.c for alternative implementations, and the memory management pages of * https://www.FreeRTOS.org for more information. */ ( void ) pv;  /* Force an assert as it is invalid to call this function. */ configASSERT( pv == NULL );}
因为它只实现了pvPortMalloc而没实现vPortFree,所以这个vPortFree是没用的,仅仅是个兼容性接口而已。
接着,把后面的heap_2~heap_5的编译运行问题一个个搞定了。
Note:我把整个工程打包好了,关注公众号“嵌入式软件实战派”,在后台回复“heap”即可获得下载链接。
通过输出的log信息,我还细心地做了个内存分配动图。
搞完这一波,看了下时间,竟然是晚上12点了。
然后,我居然兴奋得睡不着,想着怎么跟她讲解这个技术问题。
第二天,突发奇想,约她在公司附近的咖啡馆!她居然爽快地答应了!!
3. 促膝长谈晚上下班,我们一起到了咖啡厅,就像一对小……伙伴程序员,背着笔记本电脑。跟异性来这种地方,我还有些不好意思。
为了避免尴尬,单刀直入,打开笔记本,把准备好的材料跟她一一讲解。
“师兄,我们不点个咖啡吗?”
“呵呵呵……其实……你喜欢和什么?”
……

她说晚上不喝咖啡,于是我给她点了一杯奶茶,然后迫不及待地跟她将这个heap的情况。

一开始,我用官方的文档解释给她说明了下总体的情况:

我问她,“你知道FreeRTOS为什么不用C库中的malloc()free()函数来分配和释放内存吗?”

“因为,不安全。”

我听她这么肯定的说,想着她肯定是对RTOS有很深入的认识的。

是的,C库中的malloc()free()函数:

1. they are not always available on embedded systems,

2. they take up valuable code space,

3. they are not thread safe, and

4. they are not deterministic (the amount of time taken to execute the function will differ from call to call)

FreeRTOS
“所以,FreeRTOS的几个heap是为了解决这几个问题的。”
她点了点头,然后低头喝了一口奶茶。我看她那手捧奶茶的温柔,恰好点缀了窗外的夕阳……多想多停留在这一刻,但我怕她看到我在看她,接着说:“那么,这几个heap有什么区别呢?总的来说是这样的。”

heap_1 - 最简单的实现形式,不支持Free内存;

heap_2 - 允许内存Free,但不会合并free的内存块;

heap_3 - 是malloc() 和free() 的抽象层,多加了线程安全措施;

heap_4 - 合并free的块,避免碎片

heap_5 - 类似heap_4,增加了块内存段操作。

“嗯!”她轻声说。其实她已经看过了这些解释了,但是不是很理解,她想深入一点的,深入一点的学习。“‘Talk is cheap’,那么,我们直接上代码吧。”
她呵呵大笑。笑的样子也特别可爱。
“我们就按照官方的解释,来验证下。我写了段测试代码,你看看这段log就知道了。”
Base Addr: 0x407080Alloc : addr=0x00407080, size:0x01, remain:0x27F0Alloc : addr=0x00407088, size:0x02, remain:0x27E8Alloc : addr=0x00407090, size:0x04, remain:0x27E0Alloc : addr=0x00407098, size:0x08, remain:0x27D8Alloc : addr=0x004070A0, size:0x10, remain:0x27C8Alloc : addr=0x004070B0, size:0x20, remain:0x27A8Free : addr=0x00407080, remain:0x27A8Alloc : addr=0x004070D0, size:0x01, remain:0x27A0Free : addr=0x00407090, remain:0x27A0Alloc : addr=0x004070D8, size:0x02, remain:0x2798Free : addr=0x00407098, remain:0x2798Alloc : addr=0x004070E0, size:0x04, remain:0x2790Alloc : addr=0x004070E8, size:0x04, remain:0x2788Free : addr=0x004070A0, remain:0x2788Alloc : addr=0x004070F0, size:0x01, remain:0x2780Alloc : addr=0x004070F8, size:0x02, remain:0x2778Alloc : addr=0x00407100, size:0x04, remain:0x2770Alloc : addr=0x00407108, size:0x08, remain:0x2768Alloc : addr=0x00407110, size:0x10, remain:0x2758Alloc : addr=0x00407120, size:0x01, remain:0x2750Free : addr=0x004070F8, remain:0x2750Free : addr=0x00407100, remain:0x2750Free : addr=0x00407108, remain:0x2750Free : addr=0x00407110, remain:0x2750Alloc : addr=0x00407128, size:0x06, remain:0x2748Alloc : addr=0x00407130, size:0x0A, remain:0x2738
她准备站起来,把头凑过来看,我把笔记本转过去给她。然后我继续解释说,“这个Free是没有意义的”。
“哦?”她似乎有些不相信,然后试图口算这些地址值。
“我再给个图你看看。”

“哇哦……这颜色是什么意思,代表申请的内存块吗?怎么还有这么多空隙的?”她低声问道,似乎有点好奇,又有点不是很相信的样子。
“是的,要颜色的地方是表示内存申请占有的地方,至于空隙嘛,我一会再跟你解释下。”我接着说,“歪着头怎么看呢!要不坐过来我这边,我还有一个动图。”

“厉害!这都可以做出来,走心了……”
“你仔细看,其实这个vPortFree执行的地方,内存是没有变化的。”
“真的哦,heap_1是不能释放内存的。”突然,我觉得我很有成就感。
“那么,为什么会有这么多空隙的呢?我们看看这段代码就清楚了。”
void * pvPortMalloc( size_t xWantedSize ){ void * pvReturn = NULL; static uint8_t * pucAlignedHeap = NULL;  /* Ensure that blocks are always aligned. */ #if ( portBYTE_ALIGNMENT != 1 ) { if( xWantedSize & portBYTE_ALIGNMENT_MASK ) { /* Byte alignment required. Check for overflow. */ if ( (xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )) > xWantedSize ) { xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } else { xWantedSize = 0; } } } #endif
“哦……原来最大多占了一个portBYTE_ALIGNMENT的空间。”她端详了一阵这个代码说。
“所以,官方有说,自从有了static的内存分配方法,就很少用这个heap_1了。”
“嗯!”她似乎很满足的样子,伸手把奶茶拿过来,接着喝了一口。
我也喝了一口咖啡,突然觉得这个咖啡不苦了。她起身,准备要坐回去。
“等下,我这还有其他数据,给你看看heap_2的情况。”我说后,她慢慢坐了下来。夕阳已慢慢落在了城市的高楼之中,道路上依然车水马龙,而安静的咖啡馆有种说不出来的温馨。
“那么heap_2有什么不一样呢?我们直接看图吧。”

“咦?怎么有更多的孔隙了?”
“是啊,我也纳闷,但看了下源码,确实会这样,其中有一段是这样的。”
 /* The wanted size must be increased so it can contain a BlockLink_t * structure in addition to the requested amount of bytes. */ if( ( xWantedSize > 0 ) && ( ( xWantedSize + heapSTRUCT_SIZE ) >  xWantedSize ) ) /* Overflow check */ { xWantedSize += heapSTRUCT_SIZE;  /* Byte alignment required. Check for overflow. */ if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) ) > xWantedSize ) { xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); } else { xWantedSize = 0; } }
我接着解释说,“你看,用heap_2每次申请一段内存,都要多占一段heapSTRUCT_SIZE和一段portBYTE_ALIGNMENT 空间。”
“哦,怪不得,我们项目定义了32K的RAM都差不多用完了,我算了下实际的分配,还差很远,原来问题在这呢!”她似乎有种茅舍顿开的感觉。
“那么heap_3又是怎样的呢?”她主动问我要图片来看了。

“我用PC上位机软件模拟的,heap_3用的是标准库的malloc()和free(),似乎没啥规律,其实没什么参考价值的。”
“好吧……那heap_4呢?”她似乎有点失落的样子,而又“肆无忌惮”起来,直问我要图看。
我不慌不忙打开了这个图。

“好像跟heap_2一样的哦……”
“是的,很像。但是,有个很重要的区别,你看这最后一帧内容,特别是最后紫色的这块。”我特意将heap_2的和heap_4的拿出来对比了下。

“看起来似乎heap_4更省空间……哈哈!”看着这两个图的对比,她乐了起来,像个天真的孩子。
“虽然还是很多孔隙,但是free掉的空间是可以合并的,这个heap_4的好处就在这了。”
“是的,师兄你真厉害!”
“哈哈!”
“对了,还有heap_5的呢?”

“哦?跟heap_4的没啥区别哦……”这说话拉长的语调,越来越可爱了。

“效果看起来,确实是一样的,但是heap_5是可以跨不连续区域的。官方文档也是这么说的。”

This scheme uses the same first fit and memory coalescence algorithms as heap_4, and allows the heap to span multiple non adjacent (non-contiguous) memory regions.

FreeRTOS
“那么,它是怎么跨区域的呢?”她突然好奇起来。
“给你看段代码吧。其实它多了一个函数。”
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
“这个const HeapRegion_t * const pxHeapRegions就是定义不同区域段的。”
“怎么分段的呢?”“那,我们看这个官方的例子。”
// HeapRegion_t xHeapRegions[] =// {//     { ( uint8_t * ) 0x80000000UL, 0x10000 }, << Defines a block of 0x10000 bytes starting at address 0x80000000//     { ( uint8_t * ) 0x90000000UL, 0xa0000 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000//     { NULL, 0 }                << Terminates the array.// };// vPortDefineHeapRegions( xHeapRegions ); << Pass the array into vPortDefineHeapRegions().


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

(全球TMT2023年8月1日讯)7月27日,黑芝麻智能RTOS Microkernel产品获得DEKRA德凯颁发的ASIL D功能安全产品认证。本次认证通过标志着黑芝麻智能可以为客户提供一款高实时性、高安全性的本土操...

关键字: KERNEL MICRO RTOS 智能汽车

(全球TMT2022年11月9日讯)近日,DEKRA德凯为黑芝麻智能科技颁发ISO 26262:2018 ASIL D功能安全流程认证证书,标志着黑芝麻智能科技建立起了符合功能安全最高等级ASIL D级别的车规芯片开发...

关键字: 智能科技 ASIL ISO RTOS

有深入理解RTOS原理,或阅读过RTOS源码的同学应该知道:RTOS实现任务间通信通常是由一系列指针进行操作实现的。

关键字: RTOS 指针 数组

STM32CubeIDE在stm32开发者起着最基础的作用,在STM32CubeIDE中配置FreeRTOS中间层时需要选择interface,其中有三个选项:Disable、CMSIS_V1和CMSIS_V2

关键字: STM32CubeIDE FreeRTOS interface

很多RTOS之所以可以用于资源很少的单片机,是因为它们可以配置,一般可以使用宏定义来选择需要的功能,而裁剪掉不必要的功能,以减少对硬件的资源占用。

关键字: RTOS 单片机

处理特定任务的单片机可减轻主单片机或微处理器的任务和工作负荷,从而有助于简化各种应用的设计流程。

关键字: Microchip 单片机 RTOS

关注星标公众号,不错过精彩内容作者|strongerHuang微信公众号 | 嵌入式专栏不知道大家有没有做过低功耗产品?低功耗产品看似很简单,其实,要做好一款低功耗产品,特别是做到超低功耗,难度相对更高。今天就来讲讲在R...

关键字: RTOS 低功耗设计 单片机 CPU

关注星标公众号,不错过精彩内容作者|strongerHuang微信公众号 | 嵌入式专栏不知道大家有没有做过低功耗产品?低功耗产品看似很简单,其实,要做好一款低功耗产品,特别是做到超低功耗,难度相对更高。今天就来讲讲在R...

关键字: RTOS 低功耗设计

星标「嵌入式大杂烩」,一起进步!作者|strongerHuang微信公众号|嵌入式专栏经常在交流群都会看到有些小伙伴在问:**资料、**文档、**源码在哪里下载?资料、文档、源码在哪里找?很多初学小白,找资料、文档、源码...

关键字: RTOS 开源

AzureRTOS使资源受限的设备能够连接到微软的AzureIoT,这是全球领先的IoT生态系统之一。AzureRTOS为企业级应用程序(如航空电子设备、医疗设备、运输和工业控制)提供理想的软件开发平台而闻名,然而,其设...

关键字: RTOS 硬件 物联网设备
关闭