今天分享一点关于RTOS内存管理方面的内容。
阅读本文之前,建议先了解一下RTOS抢占式调度机制和时间片调度机制等相关内容。
uint8_t buffer[128];
当你写下这行代码时,就意味着你使用了128字节的静态内存,buffer的空间由编译器静态分配到栈中,且程序运行过程中,buffer的大小无法改变,这就称之为静态内存。
void *buffer_ptr = NULL;
buffer_ptr = malloc(128 * sizeof(uint8_t));
当你写下这行代码时,就意味着你使用了128字节的动态内存,buffer在程序运行时从堆中分配指定大小的空间,且不用的时候可以使用free释放,归还给堆空间。
静态内存由编译器分配,这个没什么好说的~
一般情况下使用malloc申请分配动态内存有两个缺陷:
① 由于分配算法的复杂度和堆空间的使用情况,分配的时间不定;
② 在不断申请、释放的过程中,容易因为内存对齐而产生碎片化内存;
这两个缺陷在「实时」操作系统中是不允许的,所以操作系统必须提供一套有效、合理、时间可确定的动态内存管理机制。
既然传统malloc存在两个缺陷,那就抱着解决这两个缺陷的目的出发,去建立一套更适合于嵌入式系统的动态内存管理系统。
目前有两种不同的解决方案:动态内存堆管理算法(mmheap)和静态内存池管理算法(mmblk),两种方法各有优缺点,TencentOS-tiny中两种管理算法都提供,接下来依托具体算法进行讲述。
TLSF全称Two-Level Segregated Fit memory allocator,两级隔离Fit内存分配器,是一款通用的动态内存分配器,专门设计用于满足实时要求。
❝https://github.com/mattconte/tlsf
❞
这款TLSF动态内存分配器具有以下特点:
TLSF主要采用两级位图(Two-Level Bitmap)与分级空闲块链表(Segregated Free List)的数据结构管理动态内存池(memory pool)以及其中的空闲块(free blocks),用Good-Fit的策略进行分配。
本文不对此算法进行深入讲解(博主太菜~),如果感兴趣可以查找TLSF算法论文阅读,这里我只从应用角度给出一些需要注意的点:
① 「TLSF算法分配速度不一定快,只是说能保证分配的时间是个常数」(malloc不能保证);
② TLSF也叫多内存堆管理算法,「支持动态增加或者删除多块不连续的内存」,将它们作为一个内存堆使用;
TencentOS-tiny中此算法的实现的动态内存分配器在tos_mmheap.h
和tos_mmheap.c
中,其中默认指定最大可管理的不连续内存堆有三个,可以自行修改:
#define K_MMHEAP_POOL_MAX 3
TencentOS-tiny中提供了默认的一个缓冲区作为堆空间,在tos_global.c
中定义:
uint8_t k_mmheap_default_pool[TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE] __ALIGNED__(4);
大小在tos_config.h
中指定:
#define TOS_CFG_MMHEAP_EN 1u
#define TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE 0x8000
/**
*@brief 打印当前mmeheap使用情况
*@param none
*@retval none
*/
int list_mmheap_info(void)
{
k_err_t err;
k_mmheap_info_t mmheap_info;
err = tos_mmheap_check(&mmheap_info);
if (err != K_ERR_NONE) {
printf("current mmheap info check fail, err = %d\r\n", err);
return -1;
}
else {
printf("current mmheap info:\r\n\tused: %d[0x%08x] free:%d[0x%08x]\r\n\r\n", mmheap_info.used, mmheap_info.used, mmheap_info.free, mmheap_info.free);
return 0;
}
}
void task1_entry(void *arg)
{
void *ptr = NULL;
size_t size;
/* 输出块大小的最大值 */
printf("K_MMHEAP_BLK_SIZE_MAX is %d(0x%08x) bytes\r\n\r\n", K_MMHEAP_BLK_SIZE_MAX, K_MMHEAP_BLK_SIZE_MAX);
/* 打印当前内存使用情况 */
list_mmheap_info();
/* 申请一块内存使用 */
size = 128;
ptr = tos_mmheap_alloc(size);
if (ptr == NULL) {
printf("%d bytes mem alloc fail\r\n", size);
}
else {
printf("%d bytes mem alloc success, ptr is 0x%08x\r\n\r\n", size, (uint32_t)ptr);
}
/* 打印当前内存使用情况 */
list_mmheap_info();
/* 释放申请的内存 */
tos_mmheap_free(ptr);
printf("mem free success\r\n\r\n");
/* 打印当前内存使用情况 */
list_mmheap_info();
while (1) {
tos_task_delay(1000);
}
}
运行结果为:
静态内存池就是将一块内存划分为n个大小相等的块,用户可以动态的申请、释放一个块,假装在使用动态内存。
TencentOS-tiny中静态内存池管理算法的实现在tos_mmblk.h
和tos_mmblk.c
中。
提供如下四个API:
//创建一个内存池
__API__ k_err_t tos_mmblk_pool_create(k_mmblk_pool_t *mbp, void *pool_start, size_t blk_num, size_t blk_size);
//销毁一个内存池
__API__ k_err_t tos_mmblk_pool_destroy(k_mmblk_pool_t *mbp);
//申请内存池中的一个空闲块
__API__ k_err_t tos_mmblk_alloc(k_mmblk_pool_t *mbp, void **blk);
//释放回内存池一个内存块
__API__ k_err_t tos_mmblk_free(k_mmblk_pool_t *mbp, void *blk);
typedef struct blk_st {
int id;
char* payload;
} blk_t;
#define BLK_NUM 10
k_mmblk_pool_t mmblk_pool;
uint8_t mmblk_pool_buffer[BLK_NUM * sizeof(blk_t)];
void task1_entry(void *arg)
{
blk_t *ptr = NULL;
k_err_t err;
/* 打印出一个块的大小 */
printf("block size is %d bytes\r\n", sizeof(blk_t));
/* 申请一个块 */
err = tos_mmblk_alloc(&mmblk_pool, (void*)&ptr);
if (err != K_ERR_NONE) {
printf("a mmblk alloc fail, err = %d\r\n", err);
return;
}
else {
printf("a mmblk alloc success\r\n");
}
/* 使用该块 */
ptr->id = 1;
ptr->payload = "hello";
printf("mmblk id:%d payload:%s\r\n", ptr->id, ptr->payload);
/* 使用完毕之后释放 */
err = tos_mmblk_free(&mmblk_pool, ptr);
if (err != K_ERR_NONE) {
printf("a mmblk free fail, err = %d\r\n", err);
return;
}
else {
printf("a mmblk free success\r\n");
}
while (1) {
tos_task_delay(1000);
}
}
运行结果如图:
本节主要讲述了使用malloc和free的缺点:申请时间未知,内存容易产生碎片。
所以在实时操作系统中诞生了动态堆管理机制和静态内存池管理机制,两种比较如下:
① 内存堆(mmheap)管理机制,每次可以申请和释放「不定大小的内存」,分配时间虽然不快,但是能保证已知;
② 静态内存池(mmblk)管理机制,每次只能申请和释放一个块(「固定大小的内存」),分配时间最快,没有碎片。
1.嵌友们,BUG不好找?那是因为宏定义没用好!
2.GD32V RISC-V MCU调试体验
3.2022年六大值得关注的边缘计算趋势
4.2021年第12期《单片机与嵌入式系统应用》电子刊新鲜出炉!
5.STM32的SPI外设片选只有一个,怎么破?
6.C语言开发单片机,为什么都是全局变量形式?
免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。