RT-Thread-内存管理分析

本文深入探讨RT-Thread操作系统的内存管理机制,包括小堆内存管理模块如何针对资源有限的系统进行高效管理,以及rt_system_heap_init(), rt_malloc(), rt_calloc(), rt_free()等关键函数的工作原理。

计算机系统内存管理

在计算系统中,通常存储空间可以分为两种:内部存储空间和外部存储空间。内部存储空间通常访问速度比较快,能够按照变量地址随机地访问,也就是我们通常所说的 RAM(随机存储器),可以把它理解为电脑的内存;而外部存储空间内所保存的内容相对来说比较固定,即使掉电后数据也不会丢失,这就是通常所讲的 ROM(只读存储器),可以把它理解为电脑的硬盘。

内存堆管理

内存堆管理用于管理一段连续的内存空间,在第三章中介绍过 RT-Thread 的内存分布情况,如下图所示,RT-Thread 将 “ZI 段结尾处” 到内存尾部的空间用作内存堆。
在这里插入图片描述

小堆内存管理

  1. 小堆内存管理模块主要针对系统资源比较少,一般用于小于2MB内存空间的系统,是rt-thread操作系统默认堆内存管理算法,当有可用的内存的时候,会从中分割一块来作为分配的内存,而剩下的则返回到动态内存堆中.算法采用了一个静态链表来实现的,其源码文件在根目录下的src目录下,包含mem.c和mem.h两个文件.
  2. 警告:因为动态内存管理器要满足多线程情况下的安全分配,会考虑多线程间的互斥问题,所以请不要在中断服务例程中分配或释放动态内存块。因为它可能会引起当前上下文被挂起等待。
    初始时,它是一块大的内存,其大小为(MEM_SIZE),当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来(内存块链表),具体见图
    24-2 和图 24-3。
    在这里插入图片描述
    在这里插入图片描述

heap_mem 结构体

#define HEAP_MAGIC 0x1ea0
struct heap_mem
{
   
   
    /* magic and used flag */
    rt_uint16_t magic;			/* 如果此内存块被分配了,则置0x1ea0,以此标志此块内存是正常分配出来的,而 */
    rt_uint16_t used; 			/*0:未分配;1:已分配  */

    rt_size_t next, prev;		/* 前一内存块,后一内存块  */

#ifdef RT_USING_MEMTRACE
    rt_uint8_t thread[4];   		/* thread name */
	rt_uint8_t fun_name[12];		/* 申请函数名 */
	rt_uint32_t line;				/* 申请函数地址 */
#endif
};
  • magic – 变数(或称为幻数),它会被初始化成 0x1ea0(即英文单词 heap),用于标记这个内存块是一个内存管理用的内存数据块;
  • used - 指示出当前内存块是否已经分配。

magic 变数不仅仅用于标识这个数据块是一个内存管理用的内存数据块,实质也是一个内存保护字:如果这个区域被改写,那么也就意味着这块内存块被非法改写(正常情况下只有内存管理器才会去碰这块内存)

rt_system_heap_init()初始化动态内存堆

在使用堆内存时,必须要在系统初始化的时候进行堆内存的初始化,一般在系统初始化的时候就分配一大块内存作为堆内存,然后调用 rt_system_heap_init()函数进行系统堆内存初始化,之后我们才能去申请内存,在初始化的时候需要用户自己知道初始化的是哪段内存,所以必须知道内存的起始地址与结束地址,这个函数会把参数 begin_addr,end_addr区域的内存空间作为内存堆来使用,

    /* Heap initialization */
#if defined(RT_USING_HEAP)
    rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
#endif
 * @ingroup SystemInit
 *
 * 系统内存堆的配置和初始化,该函数将初始化系统内存堆。
 *
 * @param begin_addr 系统内存堆的起始地址
 * @param end_addr 系统内存堆的结束地址。
 */
void rt_system_heap_init(void *begin_addr, void *end_addr)
{
   
   
    struct heap_mem *mem;
    /* 得到对齐后的堆内存起始地址 */
    rt_uint32_t begin_align = RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE);
    /* 得到对齐后的堆内存末尾地址 */
    rt_uint32_t end_align = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_ALIGN_SIZE);

	/* 确保此函数不是运行在中断例程内 */
    RT_DEBUG_NOT_IN_INTERRUPT;
 	 /* 确保可用动态堆内存大小至少大于可等于2个内存块控制结构大小 */
    if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&
        ((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align))
    {
   
   
        /* 计算出可用的真实可用作动态分配的内存大小 */
        mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM;
    }
    else
    {
   
   
        rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n",
                   (rt_uint32_t)begin_addr, (rt_uint32_t)end_addr);

        return;
    }

    /* heap_ptr为静态全局变量,指用堆内存起始地址 */
    heap_ptr = (rt_uint8_t *)begin_align;

    RT_DEBUG_LOG(RT_DEBUG_MEM, ("mem init, heap begin address 0x%x, size %d\n",
                                (rt_uint32_t)heap_ptr, mem_size_aligned));

    /* initialize the start of the heap */
    /* 将动态堆内存首地址初始化为一个内存块,可用大小为全部可用大小 */
    mem        = (struct heap_mem *)heap_ptr; 
    mem->magic = HEAP_MAGIC;	/* 初始化为0x1ea0 */
    mem->next  = mem_size_aligned + SIZEOF_STRUCT_MEM;/* 下一个指向动态堆内存最靠末尾的内存控制结构 */
    mem->prev  = 0;/*无前一个内存块  */
    mem->used  = 0;/*初始化为未使用  */
#ifdef RT_USING_MEMTRACE
    rt_mem_setname(mem, "INIT");
#endif

    /* initialize the end of the heap */
    heap_end        = (struct heap_mem *)&heap_ptr[mem->next]; /* 指向末尾内存块 */
    heap_end->magic = HEAP_MAGIC; /*  */
    heap_end->used  = 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值