单片机实现动态内存管理

这篇具有很好参考价值的文章主要介绍了单片机实现动态内存管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.简介

        多数传统的单片机并没有动态内存管理功能。单片机通常具有有限的存储资源,包括固定大小的静态RAM(SRAM)用于数据存储和寄存器用于特定功能。这些资源在编译时被分配并且在程序的整个生命周期中保持不变。

2.动态内存管理好处

  1. 灵活性和效率:动态内存管理可以根据程序的需要,在运行时动态分配和释放内存空间。这种灵活性使得程序能够更高效地利用可用的内存资源,避免了静态分配固定大小内存的限制。

  2. 节省内存空间:动态内存管理允许程序只在需要时分配内存,释放不再使用的内存。这样可以避免静态内存分配导致的内存浪费,提高内存利用率。

  3. 支持动态数据结构:许多数据结构,如链表、树等,大小在运行时无法预先确定,需要动态分配内存以存储变量数量可变的元素。动态内存管理使得这些动态数据结构的实现变得简单和高效。

  4. 扩展性:使用动态内存管理,可以根据程序的需求动态地调整内存大小。这使得程序能够适应不同的输入规模和需求变化,提供更好的扩展性和灵活性。

3.如何自行实现动态内存管理 

3.1 步骤

  1. 分配内存空间:首先,需要实现一个分配内存空间的函数。该函数需要检查内存池中是否有足够的空闲内存。如果有空闲内存,则将其标记为已使用,并返回指向所分配内存块的指针。如果没有足够的空闲内存,则需要采取相应的策略,例如返回空指针或扩展内存池。

  2. 释放内存空间:当不再需要分配的内存块时,需要实现一个释放内存空间的函数。该函数需要接收指向待释放内存块的指针,并将其标记为空闲状态,以便后续的内存分配可以再次利用它。

  3. 管理内存池:需要设计和管理一个内存池,也称为内存堆。内存池是一个预先分配的内存区域,用于存储动态分配和释放的内存块。需要跟踪每个已分配内存块的状态(已使用或空闲),以及其大小和地址信息。

  4. 错误处理和边界检查:在实现动态内存管理时,必须考虑各种错误情况和边界条件。例如,分配失败、重复释放内存、越界访问等。需要在代码中加入相应的检查和错误处理机制,以确保内存管理的正确性和安全性。

 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;
}
  1. 首先,通过调用getMaxFreeBlockSize函数获取当前可用的最大内存块的大小,以判断是否能够满足请求的内存大小。

  2. 如果最大可用内存块的大小大于等于所需的内存大小size,则尝试分配内存。

  3. 通过getFreeBlock函数查找一个合适大小的空闲内存块node,并返回该内存块的指针。如果没有足够大的空闲内存块,则打印错误消息并返回NULL

  4. 将找到的内存块node标记为已使用(valid = 1)。

  5. 计算实际分配的内存块大小currentSize,并判断是否需要对内存块进行分割。

  6. 如果需要进行分割,将原内存块的长度更新为currentSize,并在其后创建一个新的空闲内存块free_block,其长度为原内存块长度减去currentSizesizeof(MemBlockList)的大小。

  7. 返回分配的内存块的起始地址,即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);
        }
	}
}
  1. 首先判断参数ptr是否为NULL,如果是NULL,则直接返回。

  2. 接下来,检查ptr指向的内存地址是否在内存管理器所管理的内存范围内(g_memoryManager.startg_memoryManager.end之间)。如果不在范围内,则打印错误消息并返回。

  3. 计算ptr对应的原始内存节点source_node的地址,即ptr指针向前偏移sizeof(MemBlockList)字节。

  4. 遍历内存块链表,找到source_node的前一个节点previous_node和下一个节点next_node

  5. source_node标记为未使用(valid = 0)。

  6. 根据前一个节点previous_node和下一个节点next_node的状态,进行内存块合并操作。

    • 如果前一个节点previous_node存在且为空闲(valid = 0),则将source_node与前一个节点连接,并将原来的内存长度包括前一个节点的长度进行合并。

    • 否则,将source_node单独作为一个内存块,且不与前一个节点连接。

  7. 最后,调用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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FreeRTOS学习之路,以STM32F103C8T6为实验MCU(序章——浅谈单片机以及FreeRTOS)

    学习之路主要为FreeRTOS操作系统在STM32F103(STM32F103C8T6)上的运用,采用的是标准库编程的方式,使用的IDE为KEIL5。 注意!!!本学习之路可以通过购买STM32最小系统板以及部分配件的方式进行学习,也可以通过Proteus仿真的方式进行学习。 后续文章会同时发表在个人博客(jaso

    2024年02月06日
    浏览(54)
  • 基于单片机的智能停车场管理系统的设计与实现_kaic

    摘 要 本设计基于RFID智能识别和高速的视频图像和存储比较相结合,通过计算机的图像处理和自动识别,对车辆进出停车场的收费、车牌识别和车位诱导等,以实现停车场全方位智能管理。 本设计是以AT89C51型单片机为主控芯片的智能停车场系统,主要是针对车辆诱导和车辆检

    2024年02月06日
    浏览(35)
  • 低端单片机彩色屏幕的内存占用疑惑

    问题: 假设320*240的rgb565屏幕,320*240*2=153600,内存已经150K了,而很多低端单片机接口速度虽然勉强能用,但内存只有20K/8K,整屏的显存是绝对放不下的,只刷一部分都占很多内存,低端单片机刷彩屏都是写哪刷哪静态刷新的吗?这种场合比较合适的解决方法是什么?外挂ra

    2024年01月19日
    浏览(45)
  • (第48-59讲)STM32F4单片机,FreeRTOS【事件标志、任务通知、软件定时器、Tickless低功耗】【纯文字讲解】【】

    【吐血总结】FreeRTOS难点、Systick中断-滴答定时器、PendSV中断-任务切换、SVC中断-系统底层、时间片调度-时钟节拍【已完结】 (第1-8讲)STM32F4单片机,FreeRTOS基础知识总结【视频笔记、代码讲解】【正点原子】【原创】 (第9-10讲)STM32F4单片机,FreeRTOS任务创建和删除(动态方

    2024年02月01日
    浏览(61)
  • 不同数据类型在单片机内存中占多少字节?

    在C语言中,数据类型指的是用于声明不同类型的变量或者函数的一个广泛的系统。 变量的类型决定了变量存储占用的空间 类型 16位编译器大小 32位编译器大小 64位编译器大小 char 1个字节 1个字节 1个字节 char*(即指针变量) 2个字节 4 个字节(32位的寻址空间是2^32,即32个bi

    2024年02月07日
    浏览(37)
  • 使用gcc 工具链开发单片机程序,怎么查看内存映像

    一、概述 1.1 功能 1.2 命令格式 1.3 支持的目标文件 二、基本应用示例 2.1 查看单个对象文件 2.2 查看整个工程的内存映像 三、命令选项描述 3.1 以不同的进制格式显示内存映像 示例:以十六进制格式显示 3.2 不同的输出方式 3.2.1 可选的表示方式与对应的选项 3.2.2 -A (–format=

    2024年01月19日
    浏览(49)
  • C语言+单片机-内存分布详解,全网最全,值得收藏保存

    目录 一、C语言内存分区 1. 代码区 2. 常量区 3. 全局(静态)区 4. 堆区(heap) 5. 栈区(stack) 二、STM32存储器分配 1. 随机存储器—RAM 2. 只读存储器—ROM 三、基于STM32代码验证 1. 详细代码如下 2. 运行结果如下 四、单片机中的内存分布 1.含义解释 2. 程序存储分布 3.程序占用Flash和SRA

    2024年02月09日
    浏览(38)
  • 【51单片机】动态数码管

    0、前言 参考: 普中51单片机开发攻略–A2.pdf 上一章我们主要是介绍一位数码管的内部结构及控制原理。下面我们再来介 绍下多位数码管及动态显示原理的相关知识。 本章所要实现的功能是:控制动态数码管从左至右显示数字 0-7。 为了正规点,工程弄个正规文件夹: http

    2024年01月21日
    浏览(67)
  • 单片机——数码管动态显示

    1.头文件和定义,代码还使用了 sbit 定义来声明了两个变量 duan 和 wei,它们实际上是 P2.6 和 P2.7 端口的别名,用于控制数码管的段选和位选信号。这种方法可以使代码更具可读性,并提高代码的可维护性。定义无符号整数便于后面应用。 2.使用一个 16 个元素的数组 table,该数

    2024年02月11日
    浏览(40)
  • 51单片机控制数码管动态显示

    首先打开proteus,导入8位数码管和89c51。 然后如图连线,分清断码和位码, 断码就是一个数码管的7个LED灯。 位码:就是第几位显示,由于是共阴极,所以哪位接地就显示哪位。 下面通过改变位码的接线就可以看出不同的效果 下面就编写程序,从第1位到第8位显示从0到7的八

    2023年04月21日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包