1.简介
多数传统的单片机并没有动态内存管理功能。单片机通常具有有限的存储资源,包括固定大小的静态RAM(SRAM)用于数据存储和寄存器用于特定功能。这些资源在编译时被分配并且在程序的整个生命周期中保持不变。
2.动态内存管理好处
-
灵活性和效率:动态内存管理可以根据程序的需要,在运行时动态分配和释放内存空间。这种灵活性使得程序能够更高效地利用可用的内存资源,避免了静态分配固定大小内存的限制。
-
节省内存空间:动态内存管理允许程序只在需要时分配内存,释放不再使用的内存。这样可以避免静态内存分配导致的内存浪费,提高内存利用率。
-
支持动态数据结构:许多数据结构,如链表、树等,大小在运行时无法预先确定,需要动态分配内存以存储变量数量可变的元素。动态内存管理使得这些动态数据结构的实现变得简单和高效。
-
扩展性:使用动态内存管理,可以根据程序的需求动态地调整内存大小。这使得程序能够适应不同的输入规模和需求变化,提供更好的扩展性和灵活性。
3.如何自行实现动态内存管理
3.1 步骤
-
分配内存空间:首先,需要实现一个分配内存空间的函数。该函数需要检查内存池中是否有足够的空闲内存。如果有空闲内存,则将其标记为已使用,并返回指向所分配内存块的指针。如果没有足够的空闲内存,则需要采取相应的策略,例如返回空指针或扩展内存池。
-
释放内存空间:当不再需要分配的内存块时,需要实现一个释放内存空间的函数。该函数需要接收指向待释放内存块的指针,并将其标记为空闲状态,以便后续的内存分配可以再次利用它。
-
管理内存池:需要设计和管理一个内存池,也称为内存堆。内存池是一个预先分配的内存区域,用于存储动态分配和释放的内存块。需要跟踪每个已分配内存块的状态(已使用或空闲),以及其大小和地址信息。
-
错误处理和边界检查:在实现动态内存管理时,必须考虑各种错误情况和边界条件。例如,分配失败、重复释放内存、越界访问等。需要在代码中加入相应的检查和错误处理机制,以确保内存管理的正确性和安全性。
3.2方案
1.增加外部SRAM或者DRAM,使用外部空间来作为动态内存管理,这种方法需要,编写外部SRAM的驱动,并将外部的SRAM地址映射到芯片地址中。
例如以下驱动控制器:
STM32系列微控制器的FSMC(Flexible Static Memory Controller)是一种专门设计用于连接外部存储器设备(如SRAM、NOR Flash等)的控制器。
NXP LPC系列微控制器的EMC(External Memory Controller)来支持与外部存储器设备的数据交互可以连接多种类型的存储器,如SRAM、NOR Flash、SDRAM等。
TM4C系列微控制器的嵌入式外部存储器接口(EMIFA),通过该接口可以连接外部存储器设备,并支持SRAM、NOR Flash、NAND Flash等多种存储器类型。
2.使用内部SRAM的静态变量存储区来当作动态内存管理,这部分空间不允许再当静态空间来操作,这种方法无需依赖外部控制器,但是同时也无法管理更大的动态空间。
4.实现
动态内存管理的本质就是对一段地址的管理。它涉及到在运行时动态分配和释放内存空间,并管理各个内存块的状态可用性。
4.1直接使用静态变量
#define MEM_MAX_SIZE 100*1024 //100K
static unsigned char mem1base[MEM_MAX_SIZE]; //内部SRAM内存池
4.2使用外部SRAM地址需要映射
static unsigned char mem2base[MEM_MAX_SIZE] __attribute__((at(0XC0000000))); //外部SDRAM内存池
这里的地址0XC0000000只是示例 需要按照上文提到驱动控制器进行修改。
4.3 数据结构设计
typedef struct _MemBlockList
{
union
{
struct
{
unsigned int valid : 1; // 0 表示未使用 1表示使用了
unsigned int length : 31; // 长度
};
int info; // 整体的信息(部分)
};
struct _MemBlockList *next; // 下一块
} MemBlockList;//共占用8个字节
typedef struct _MemoryManager
{
void *start;
void *end;
unsigned int size;
MemBlockList *head;
} MemoryManager;
static MemoryManager g_memoryManager;
MemBlockList
用于管理一块连续的内存区域,并维护了一个内存块链表,用于跟踪空闲和已分配的内存块,MemoryManager
用于表示内存管理器。通过使用这两个结构体,可以实现对一块连续内存的管理,包括内存块的分配和释放。g_memoryManager
是一个全局的内存管理器变量,用于在程序中跟踪和管理内存块的分配和释放情况。
4.4 动态内存的初始化
bool memoryManagerInit(void)
{
int size = MEM_MAX_SIZE;
g_memoryManager.end = memXbase + size;
g_memoryManager.start = memXbase;
g_memoryManager.size = size;
g_memoryManager.head = (MemBlockList *)g_memoryManager.start;
g_memoryManager.head->length = g_memoryManager.size - sizeof(MemBlockList);
g_memoryManager.head->valid = 0;
g_memoryManager.head->next = NULL;
printf("memoryManagerInit success : %d KB\n", size / 1024);
return true;
}
4.6 动态内存的申请
void *memoryManager_malloc(int size)
{
int free_size = getMaxFreeBlockSize();
if (free_size >= size)
{
MemBlockList *ptr = g_memoryManager.head;
MemBlockList *free_block = NULL;
/* 指针对齐,保证了 currentSize 是指针 sizeof(void*) 的整数倍大小 */
int n = size / sizeof(void*);
int currentSize = n * sizeof(void*);
if (size % sizeof(void*) != 0)
{
currentSize += sizeof(void*);
}
bool isNeedCut = false; //是否需要分割
MemBlockList *node = getFreeBlock(currentSize, &isNeedCut);
if (node == NULL)
{
printf("malloc size = %d faile !!!!!\n", size);
print_mem_info();
return (void *)(NULL);
}
/* 标记内存块使用了 */
node->valid = 1;
unsigned char *p = (unsigned char *)node;
if (isNeedCut)
{
p += sizeof(MemBlockList) + currentSize;
free_block = (MemBlockList *)(p);
free_block->length = node->length - currentSize - sizeof(MemBlockList);
free_block->valid = 0;
free_block->next = node->next;
node->next = free_block;
node->length = currentSize;
}
p = (unsigned char *)node;
p += sizeof(MemBlockList);//偏移8个字节为真正使用的malloc地址
checkMem();
print_mem_info();
return (void *)(p);
}
return NULL;
}
-
首先,通过调用
getMaxFreeBlockSize
函数获取当前可用的最大内存块的大小,以判断是否能够满足请求的内存大小。 -
如果最大可用内存块的大小大于等于所需的内存大小
size
,则尝试分配内存。 -
通过
getFreeBlock
函数查找一个合适大小的空闲内存块node
,并返回该内存块的指针。如果没有足够大的空闲内存块,则打印错误消息并返回NULL
。 -
将找到的内存块
node
标记为已使用(valid = 1
)。 -
计算实际分配的内存块大小
currentSize
,并判断是否需要对内存块进行分割。 -
如果需要进行分割,将原内存块的长度更新为
currentSize
,并在其后创建一个新的空闲内存块free_block
,其长度为原内存块长度减去currentSize
和sizeof(MemBlockList)
的大小。 -
返回分配的内存块的起始地址,即
node
指针偏移sizeof(MemBlockList)
字节后的地址。
4.7 动态内存的释放
void memoryManager_free(void *ptr)
{
if (ptr != NULL)
{
if ((ptr > g_memoryManager.start) && (ptr < g_memoryManager.end))
{
/* 计算地址对应的原始节点 */
MemBlockList *source_node = (MemBlockList *)((unsigned char *)ptr - sizeof(MemBlockList));
/* 找到node的前一个节点和下一个节点 */
MemBlockList *previous_node = g_memoryManager.head;
while (previous_node && previous_node->next != source_node)
{
previous_node = previous_node->next;
}
MemBlockList *next_node = source_node->next;
checkMem();
source_node->valid = 0;
MemBlockList *connect_node;
if (previous_node && (previous_node->valid == 0))// 前一个节点是否空闲
{
connect_node = source_node->next;
int size = source_node->length + sizeof(MemBlockList);
if (next_node && (next_node->valid == 0))// 下一个节点是否空闲
{
connect_node = next_node->next;
size += next_node->length + sizeof(MemBlockList);
}
previous_node->next = connect_node;
previous_node->length += size;
}
else
{
connect_node = source_node->next;
int size = source_node->length;
if (next_node && (next_node->valid == 0))
{
connect_node = next_node->next;
size += next_node->length + sizeof(MemBlockList);
}
source_node->next = connect_node;
source_node->length = size;
}
checkMem();
}
else
{
printf("memoryManager_free not allowd this address = %p(%p --- %p)\n", ptr, g_memoryManager.start, g_memoryManager.end);
}
}
}
-
首先判断参数
ptr
是否为NULL
,如果是NULL
,则直接返回。 -
接下来,检查
ptr
指向的内存地址是否在内存管理器所管理的内存范围内(g_memoryManager.start
到g_memoryManager.end
之间)。如果不在范围内,则打印错误消息并返回。 -
计算
ptr
对应的原始内存节点source_node
的地址,即ptr
指针向前偏移sizeof(MemBlockList)
字节。 -
遍历内存块链表,找到
source_node
的前一个节点previous_node
和下一个节点next_node
。 -
将
source_node
标记为未使用(valid = 0
)。 -
根据前一个节点
previous_node
和下一个节点next_node
的状态,进行内存块合并操作。-
如果前一个节点
previous_node
存在且为空闲(valid = 0
),则将source_node
与前一个节点连接,并将原来的内存长度包括前一个节点的长度进行合并。 -
否则,将
source_node
单独作为一个内存块,且不与前一个节点连接。文章来源:https://www.toymoban.com/news/detail-632918.html
-
-
最后,调用
checkMem
函数检查内存状态。文章来源地址https://www.toymoban.com/news/detail-632918.html
5. 实现代码总览
#ifndef NULL
#define NULL ((void *)0)
#endif
typedef struct _MemBlockList
{
union
{
struct
{
unsigned int valid : 1; // 0 表示未使用 1表示使用了
unsigned int length : 31; // 长度
};
int info; // 整体的信息(部分)
};
struct _MemBlockList *next; // 下一块
} MemBlockList;//共占用8个字节
typedef struct _MemoryManager
{
void *start;
void *end;
unsigned int size;
MemBlockList *head;
} MemoryManager;
static MemoryManager g_memoryManager;
/* 获取当前内存中空闲的最大块大小 */
static unsigned int getMaxFreeBlockSize(void)
{
MemBlockList *node = g_memoryManager.head;
unsigned int size = 0;
while (node)
{
if(node->valid == 0)
{
if (node->length > size)
{
size = node->length;
}
}
node = node->next;
}
return size;
}
/* 获取当前内存中 满足size条件下 最小的内存块 */
static MemBlockList *getFreeBlock(unsigned int size, bool *isNeedCutMem)
{
MemBlockList *current_node = g_memoryManager.head;
MemBlockList *ret_node = NULL;
unsigned int min_size = 0xffffffff;
*isNeedCutMem = false;
while (current_node)
{
if (current_node->valid == 0 && current_node->length >= size) //块是否空闲
{
bool current_cut = false;
if (current_node->length >= (size + sizeof(MemBlockList) + (sizeof(void *))))
current_cut = true;
if (current_node->length < min_size)
{
min_size = current_node->length;
*isNeedCutMem = current_cut;
ret_node = current_node;
if (min_size == size)
break;
}
}
current_node = current_node->next;
}
return ret_node;
}
/* 打印当前的内存使用情况 */
void print_mem_info(void)
{
MemBlockList *node = g_memoryManager.head;
while (node)
{
printf("address = %p, valid = %d, next = %p, size = %d\n", node, node->valid, node->next, node->length);
node = node->next;
}
}
/* 内存检查 */
static void checkMem(void)
{
MemBlockList *node = g_memoryManager.head;
int size = 0;
int cnt = 0;
while (node)
{
cnt ++;
size += node->length;
node = node->next;
}
if (size < (g_memoryManager.size - cnt * sizeof(MemBlockList)))
{
printf("checkMem err now only have %d block, size = %d!!!\n", cnt, size);
print_mem_info();
while (1);
}
return;
}
/* 内存操作检查 */
static bool checkMemUse(void *ptr, int size)
{
if((ptr < g_memoryManager.start) || (ptr > g_memoryManager.end))
{
//不在管控范围内的地址不进行校验
return true;
}
MemBlockList *node = g_memoryManager.head;
unsigned char *current_p = NULL;
while (node)
{
int current_size = node->length;
current_p = (unsigned char *)node + sizeof(MemBlockList);
if ((ptr >= current_p) && (ptr < (current_p + current_size)))
{
if (node->valid == 0)
{
printf("checkMemUse this address = %p is not active !!\n", ptr);
return false;
}
else
{
if(size > (node->length - ((unsigned char *)ptr - current_p)))
{
printf("checkMemUse this address = %p size = %d, is size over, source address = %p, len = %d!!\n",
ptr, size, current_p, node->length);
return false;
}
}
}
node = node->next;
}
return true;
}
bool memoryManagerInit(void)
{
int size = MEM_MAX_SIZE;
g_memoryManager.end = memXbase + size;
g_memoryManager.start = memXbase;
g_memoryManager.size = size;
g_memoryManager.head = (MemBlockList *)g_memoryManager.start;
g_memoryManager.head->length = g_memoryManager.size - sizeof(MemBlockList);
g_memoryManager.head->valid = 0;
g_memoryManager.head->next = NULL;
printf("memoryManagerInit success : %d KB\n", size / 1024);
return true;
}
void *memoryManager_malloc(int size)
{
int free_size = getMaxFreeBlockSize();
if (free_size >= size)
{
MemBlockList *ptr = g_memoryManager.head;
MemBlockList *free_block = NULL;
/* 指针对齐,保证了 currentSize 是指针 sizeof(void*) 的整数倍大小 */
int n = size / sizeof(void*);
int currentSize = n * sizeof(void*);
if (size % sizeof(void*) != 0)
{
currentSize += sizeof(void*);
}
bool isNeedCut = false; //是否需要分割
MemBlockList *node = getFreeBlock(currentSize, &isNeedCut);
if (node == NULL)
{
printf("malloc size = %d faile !!!!!\n", size);
print_mem_info();
return (void *)(NULL);
}
/* 标记内存块使用了 */
node->valid = 1;
unsigned char *p = (unsigned char *)node;
if (isNeedCut)
{
p += sizeof(MemBlockList) + currentSize;
free_block = (MemBlockList *)(p);
free_block->length = node->length - currentSize - sizeof(MemBlockList);
free_block->valid = 0;
free_block->next = node->next;
node->next = free_block;
node->length = currentSize;
}
p = (unsigned char *)node;
p += sizeof(MemBlockList);//偏移8个字节为真正使用的malloc地址
checkMem();
print_mem_info();
return (void *)(p);
}
return NULL;
}
void memoryManager_free(void *ptr)
{
if (ptr != NULL)
{
if ((ptr > g_memoryManager.start) && (ptr < g_memoryManager.end))
{
/* 计算地址对应的原始节点 */
MemBlockList *source_node = (MemBlockList *)((unsigned char *)ptr - sizeof(MemBlockList));
/* 找到node的前一个节点和下一个节点 */
MemBlockList *previous_node = g_memoryManager.head;
while (previous_node && previous_node->next != source_node)
{
previous_node = previous_node->next;
}
MemBlockList *next_node = source_node->next;
checkMem();
source_node->valid = 0;
MemBlockList *connect_node;
if (previous_node && (previous_node->valid == 0))// 前一个节点是否空闲
{
connect_node = source_node->next;
int size = source_node->length + sizeof(MemBlockList);
if (next_node && (next_node->valid == 0))// 下一个节点是否空闲
{
connect_node = next_node->next;
size += next_node->length + sizeof(MemBlockList);
}
previous_node->next = connect_node;
previous_node->length += size;
}
else
{
connect_node = source_node->next;
int size = source_node->length;
if (next_node && (next_node->valid == 0))
{
connect_node = next_node->next;
size += next_node->length + sizeof(MemBlockList);
}
source_node->next = connect_node;
source_node->length = size;
}
checkMem();
}
else
{
printf("memoryManager_free not allowd this address = %p(%p --- %p)\n", ptr, g_memoryManager.start, g_memoryManager.end);
}
}
}
void memoryManager_cpy(void *dest, const void *src, unsigned int n)
{
if(checkMemUse(src, n) == false)
{
printf("memoryManager_cpy error check src\n");
}
if(checkMemUse(dest, n) == false)
{
printf("memoryManager_cpy error check dest\n");
}
for (unsigned int i = 0; i < n; i++)
{
dest[i] = src[i];
}
}
void memoryManager_set(void *ptr, unsigned char value, unsigned int num)
{
if(checkMemUse(ptr, num) == false)
{
printf("memoryManager_set error check ptr\n");
}
for (unsigned int i = 0; i < num; i++)
{
ptr[i] = value;
}
}
到了这里,关于单片机实现动态内存管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!