一、动态内存管理

动态内存管理是一个真实的堆(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 Dictionary Example

    Dictionary class is the abstract class which is parent of any class which uses the key and value pai ...

  2. LWM2M的DISCOVER操作

    1. 先看下DISCOVER的数据流,工作服务器下发的指令到设备客户端 2. 解释,这个操作是用来发现Object, Object Instances, and Resources的属性,同时可以发现 ...

  3. thinkphp5保存远程图片到本地

    代码 protected function saveImg($imgUrl){ $ext=strrchr($imgUrl,'.'); if(!in_array($ext,['.jpg','.png', ...

  4. Qt-QML-Button-ButtonStyle-实现鼠标滑过点击效果

    上次实现的自定义的Button功能是用的自定义的Rectangle来实现的,在慢慢的接触了QML之后,发现QML有自己定义的Button 这里盗版贴上Qt帮助文档中的部分关于Button的属性内容 B ...

  5. APP性能测试工具-GT(随身调)

    GT(随身调)是APP的随身调测平台,它是直接运行在手机上的“集成调测环境”(IDTE, Integrated Debug Environment).利用GT,仅凭一部手机,无需连接电脑,您即可对AP ...

  6. Python学习-猜数字游戏

    菩萨蛮·黄鹤楼 茫茫九派流中国,沉沉一线穿南北.烟雨莽苍苍,龟蛇锁大江. 黄鹤知何去,剩有游人处.把酒酹滔滔,心潮逐浪高! --coding:UTF-8-- import random secret ...

  7. 关于maven项目中修改的JS不生效的解决方案

    1. 问题描述 昨天下午博主在开发学习的过程中,碰到一个修改了JS无法生效的问题,折腾我不少的时间,现将百度到的解决方案总结一下,以便下次碰到类似问题能够最快的找到解决方案 2 解决方案 2.1 方案 ...

  8. css3美化radio样式

    .magic-radio{ position: absolute; display: none; } .magic-radio + label { position: relative; displa ...

  9. 11.24Daily Scrum(4)

    人员 任务分配完成情况 明天任务分配 王皓南 实现网页上视频浏览的功能.研究相关的代码和功能.1007 实现视频浏览的功能 申开亮 实现网页上视频浏览的功能.研究相关的代码和功能.1008 实现视频浏 ...

  10. UBUNTU如何安装tar.gz版的flash

    adobe flash player的官方下载页面为:https://get.adobe.com/cn/flashplayer/ 不过近期通过APT方式以及ubuntu的软件中心都安装不了flashp ...