一、动态内存管理

动态内存管理是一个真实的堆(Heap)内存管理模块,可以在当前资源满足的情况下,根据用户的需求分配任意大小的内存块。而当用户不需要再使用这些内存块时,又可以释放回堆中供其他应用分配使用。RT-Thread系统为了满足不同的需求,提供了两套不同的动态内存管理算法,分别是小内存管理算法和SLAB内存管理算法。小堆内存管理模块主要针对系统资源比较少,一般用于小于2M内存空间的系统;而SLAB内存管理模块则主要是在系统资源比较丰富时,提供了一种近似多内存池管理算法的快速算法。

两种内存管理模块在系统运行时只能选择其中之一或者完全不使用动态堆内存管理器。这两种管理模块提供的API接口完全相同。因为动态内存管理器要满足多线程情况下的安全分配,会考虑多线程间的互斥问题,所以请不要在中断服务例程中分配或释放动态内存块。因为它可能会引起当前上下文被挂起等待。

二、小内存管理算法

本文主要介绍小内存管理算法,至于SLAB内存管理算法则在后续文章中介绍。小内存管理算法是一个简单的内存分配算法。初始时,它是一块大的内存。当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来,如 内存块链表图所示:

内存管理的在表现主要体现在内存的分配与释放上,小型内存管理算法可以用以下例子体现出来。

                                                                                            

空闲链表指针lfree初始指向32字节的内存块。当用户线程要再分配一个64字节的内存块时,但此lfree指针指向的内存块只有32字节并不能满足要求,内存管理器会继续寻找下一内存块,当找到再下一块内存块,128字节时,它满足分配的要求。因为这个内存块比较大,分配器将把此内存块进行拆分,余下的内存块(52字节)继续留在lfree链表中。另外,在每次分配内存块前,都会留出12字节数据头用于magic,used信息及链表节点使用。返回给应用的地址实际上是这块内存块12字节以后的地址,前面的12字节数据头是用户永远不应该碰的部分。(注:12字节数据头长度会与系统对齐差异而有所不同)。释放时则是相反的过程,但分配器会查看前后相邻的内存块是否空闲,如果空闲则合并成一个大的空闲内存块。

数据结构:在src/mem.c中

#define HEAP_MAGIC 0x1ea0
struct heap_mem
{
/* magic and used flag */
rt_uint16_t magic; //如果此内存块被分配了,则(在rt_malloc中设置)置0x1ea0。标志该内存块为一个内存管理系统使用的动态内存数据块,类似于一个内存保护字:如果这个区域被改写,那么也就意味着这块内存块被非法改写 (正常情况下只有内存管理系统才会访问它)
rt_uint16_t used; //0:未分配;1:已分配 rt_size_t next, prev; //后一内存块首地址(包含内存块控制结构),前一内存块首地址(包含内存块控制结构)。注意这里不再是rt_list_t类型(链表类型),它们直接赋值为内存地址
};

三、小内存管理算法函数接口:在src/mem.c中

初始化动态内存堆:
void rt_system_heap_init(void *begin_addr, void *end_addr);
在使用堆内存RT_USING_HEAP时,必须要在系统初始化的时候进行堆内存的初始化。这个函数会把参数begin_addr,end_addr区域的内存空间作为内存堆来使用。
由源代码可知,初始化时小内存管理算法通过传进来的起始地址和末尾地址将动态堆内存初始化为两个内存块:第一个内存块指向动态堆内存首地址,可用空间为整个可分配的内存(不包含两个内存控制块本身所占大小,即减去24字节),此内存块下一指针指向末尾内存控制块;第二个内存块指向最末尾的一个内存控制块,可用空间大小为0,此内存块前一指针和后一指针都指向本身。 分配内存块:
void *rt_malloc(rt_size_t size);
rt_malloc函数会从系统堆空间中找到合适大小的内存块,然后把内存块可用地址返回给用户。 重分配内存块:
void *rt_realloc(void *rmem, rt_size_t newsize);
在已分配内存块的基础上重新分配内存块的大小(增加或缩小),在进行重新分配内存块时,原来的内存块数据保持不变(缩小的情况下,后面的数据被自动截断)。
由代码可知,如果当前内存块可用内存比较充裕时,将分割成两块,后一块分割出来后会尝试与前后内存块合并。 分配多块内存:
void *rt_calloc(rt_size_t count, rt_size_t size);
从内存堆中分配连续内存地址的多个内存块,返回的指针指向第一个内存块的地址,并且所有分配的内存块都被初始化成零。 释放内存:
void rt_free(void *rmem);
用户线程使用完从内存分配器中申请的内存后,必须及时释放,否则会造成内存泄漏,rt_free函数会把待释放的内存换回给堆管理器中。在调用这个函数时用户需传递待释放的内存块指针,如果是空指针直接返回。
内存合并:
static void plug_holes(struct heap_mem *mem);
此函数在重分配内存时调用,将分割出来的后一部分尝试与前后内存块合并;当释放内存时,算法将检查待释放内存的前一内存块和后一内存块,如果为空闲则合并。

四、算法总结

小内存管理算法从整体上来讲,是将一片内存初始化为静态链表来实现的,初始化时只有两个内存块:

第一块除了包含内存块控制块(占用12字节)外,还包含待分配的空间,这个空间就是mem_size_aligned,它是指此算法可用来作分配的动态堆内存总大小,任何待分配的内存都不能比它还大,否则超过极限;

第二块只包含内存控制块本身(占用12字节),不包含待分配的空间,它作为链表尾,且设置使用标志used为1(永久使用)。

整个算法中还有一空闲指针lfree,始终指向动态内存堆中剩余可用空间的第一个空闲内存块,初始化时指向动态内存堆起始地址,该指针变量在分配内存和合并内存时会不断更新。接下来就是分配内存了,分配内存时首先从空闲内存所指向的节点开始扫描,一旦扫描到大小满足的节点,则返回此节点,若此节点所指向的空间足够大,大到还有足够空间分配另一内存块(只含内存块控制结构)时,则分割此节点指向的内存为两块,前一块内存可用空间user data首地址返回,后一块内存设置为未分配(空闲内存); 当释放内存时,算法将检查待释放内存的前一内存块和后一内存块,如果为空闲则合并。

RT-thread内核之小内存管理算法的更多相关文章

  1. 源码解读·RT-Thread小内存管理算法分析

    这篇文章最初发布在RT-Thread官方论坛中,最近准备整理放到博客中来让更多人一起探讨学习. 2012年9月28日星期五 前言: 母语能力有限 概述: 这篇文字和大家分享一下今晚对RT-Thread ...

  2. Linux内核内存管理算法Buddy和Slab: /proc/meminfo、/proc/buddyinfo、/proc/slabinfo

    slabtop cat /proc/slabinfo # name <active_objs> <num_objs> <objsize> <objpersla ...

  3. rt-thread中动态内存分配之小内存管理模块方法的一点理解

    @2019-01-18 [小记] rt-thread中动态内存分配之小内存管理模块方法的一点理解 > 内存初始化后的布局示意 lfree指向内存空闲区首地址 /** * @ingroup Sys ...

  4. 自动内存管理算法 —— 标记和复制法

    最近阅读了<垃圾回收算法手册>这本经典的书籍,借此机会打算写几篇内存管理算法方面的文章,也算是自己的总结吧.                                         ...

  5. <Linux内核源码>内存管理模型

    题外语:本人对linux内核的了解尚浅,如果有差池欢迎指正,也欢迎提问交流! 首先要理解一下每一个进程是如何维护自己独立的寻址空间的,我的电脑里呢是8G内存空间.了解过的朋友应该都知道这是虚拟内存技术 ...

  6. 【原创】xenomai内核解析--实时内存管理--xnheap

    目录 一. xenomai内存池管理 1.xnheap 2. xnpagemap 3. xnbucket 4. xnheap初始化 5. 内存块分配 5.1 小内存分配流程(<= 2*PAGE_ ...

  7. Linux内核剖析 之 内存管理

    1. 内存管理区 为什么分成不同的内存管理区? ISA总线的DMA处理器有严格的限制:仅仅能对物理内存前16M寻址. 内核线性地址空间仅仅有1G,CPU不能直接訪问全部的物理内存. ZONE_DMA  ...

  8. linux内核--用户态内存管理

    在上一篇博客“内核内存管理”中,描述的内核内存管理的相关算法和数据结构,在这里简单描述用户态内存管理的数据结构和算法. 一,相关结构体 与进程地址空间相关的全部信息都包含在一个叫做“内存描述符”的数据 ...

  9. Windows内核中的内存管理

    内存管理的要点 内核内存是在虚拟地址空间的高2GB位置,且由所有进程所共享,进程进行切换时改变的只是进程的用户分区的内存 驱动程序就像一个特殊的DLL,这个DLL被加载到内核的地址空间中,Driver ...

随机推荐

  1. Java设计模式(19)——行为模式之责任链模式(chain of responsibilitiy)

    一.概述 概念 UML简图 角色 抽象处理器:定义处理请求的接口 具体处理器:接收到请求后可以选择处理,也可以选择发给下家处理(持有下家的引用) 当然这里必须指出,实际中纯的责任链模式很难寻找,一般是 ...

  2. 【NIS】深入了解NIS

    1  简介 NIS( NetworkInformation Service)提供了一个网络黄页的功能,当用户登录系统时,Linux系统会到NIS主机上去寻找用户使用的帐号密码信息加以比对,以提供用户登 ...

  3. maven私有库搭建

    一.在企业中基本上都会有自己的maven私有库,主要的目的就是方便依赖包的下载.如果采用远程的方式来实现的话,很多时候会考虑网速问题.如果自己活着公司搭建的私有库,这一样在使用上面会效率更高. 二.私 ...

  4. serv-u自动停止的解决方法

    在主界面serv-u管理控制台-主页--管理服务器----服务器详细信息下,点击“创建,修改并删除服务器事件”找到“事件”右击空白处---“添加”然后如下图所示填写: 点击“保存”就好了,而且我自己也 ...

  5. PS 去皱纹

    1.打开一个有皱纹的图片,选择修复画笔工具,按住Alt键吸取一块光滑的皮肤,然后再在有皱纹的位置上点击即可

  6. 03-JVM内存模型:堆与方法区

    一.堆(Heap) 1.1.什么是堆 堆是用于存放对象的内存区域.因此,它是垃圾收集器(GC)管理的主要目标.其具有以下特点: 堆在逻辑上划分为“新生代”和“老年代”.由于JAVA中的对象大部分是朝生 ...

  7. Linux命令应用大词典-第38章 网络命令

    38.1 traceroute:显示跟踪到网络主机的路由数据包 38.2 mli-tool:查看.操纵网络接口状态 38.3 ifconfig:显示和配置网络接口 38.4 ifdown:关闭网络接口 ...

  8. 初学Direct X (2)

    初学Direct X (2) 这一次要学习如何现实位图,尽管看过对双缓冲机制还有很多疑问,但是这并不阻碍我对他的入门了解 Direct3D提供了一个双重/后台缓冲区,在调用CreateDevice之时 ...

  9. 微信小程序之基础案例详细解释

    这是案例的初始页面 首先关于这个案例上面的app.json做一个特别详细的解释 首先提醒一下.json文件不能有注释,因此如果要复制的话,请把注释删去 //关于app.json详细学习 { //pag ...

  10. ## 在webapp上使用input:file, 指定capture属性调用默认相机,摄像,录音功能

    在iOS6下开发webapp,使用inputz之file,很有用 <input type="file" accept="image/*" capture= ...