嵌入式实时操作系统的设计与开发(七)

news2025/1/16 7:47:52

内存管理机制

内存管理就是把物理的存储资源用一定的规则和手段管理起来,以供给操作系统和应用程序使用。
主要的操作:内存的分配和内存的回收。
内存的利用率、分配回收的效率和稳定性成为了评价内存管理模块的主要依据。
内存分配又包括静态和动态两种:

  • 静态分配的方式很简单,但必须事先知道程序的运行全貌(代码大小、数据大小等),这样的系统缺乏灵活性。
  • 动态分配的方式比较灵活,也是常见的分配方式,但动态分配会耗费一些内存管理过程中额外的空间和时间开销,所以在内存特别小、实时性要求特别高的情况下还是会采用静态分配的方法。

当要分配6B的内存时,其实操作系统分配了8B,这样就会浪费2B的内存,这就是内存碎片。当多次使用malloc()后,可能会出现即使系统中的累积空闲内存远远大于1MB,但系统仍然无法分配要申请的连续的1MB空间,因为经过多次malloc()后,这1MB的内存已经被分得七零八散,这样零散的但又不能供用户使用的内存块称为外碎片。

  • 内部碎片就是已经被分配出去(明确指出属于哪个线程),却不能被利用的内存空间。
  • 外部碎片就是没有被分配出去(不属于任何线程),但由于太小了无法分配给申请内存空间的新线程的内存空闲区域。

内碎片和外碎片是内存管理的一对矛盾,减少外碎片就可能增加内碎片,除非增加很多限制条件,同时外碎片没法完全避免,只是多少的问题。
伙伴系统就是减少外碎片的内存管理算法,但是也只能将外碎片降低到最大1/2总内存大小,但是该算法会产生很大的内碎片,因此伙伴系统只能在特定场合和特定应用中使用。

主流内存管理机制

对于一些应用简单、任务数目事先确定的嵌入式系统或强实时系统,为了减少内存分配在时间上可能带来的不确定性,可采用静态内存分配方式。
静态内存分配方式在系统启动时,为系统中所有任务都分配了内存空间,系统运行过程中不会有新的内存请求,因此,操作系统不需要进行专门的内存管理操作。这种系统使用内存的效率比较低,只适合于应用简单、任务数目事先确定的嵌入式系统或强实时系统。

大多数系统都使用动态内存管理机制,而当前主流的内存管理机制又分为固定大小存储管理和可变大小存储管理。

  1. 固定大小存储管理
    固定大小存储管理方式中,内存是由一段连续的内存构成的,这段内存被分为多个大小一样的固定块,这种管理方式可以很好地解决外部碎片,因为每次分配的都是固定大小的内存,只要有空闲内存,肯定可以分配某一固定大小的空间。
    但此方法无法解决内部碎片,若开发人员只想要20B的内存,它也会分配256B。
  2. 可变大小存储管理
    又分为两种:
    (1)任意大小分配的存储管理。用户需要多少,就分配多少,解决了内碎片问题。该方式实现比较简单,在整个受管理的内存中找到一块大于用户需求的内存块,然后一分为二,一块是用户申请的内存块大小,剩下的一块是空闲的,用于以后的分配。但用户申请内存、释放内存的时间是随机的,而每一次的申请和释放都有可能产生新的空闲内存块,之后系统可能会将已有的空闲块回收、合并成新的空闲块。因此,系统中的空闲内存块数、地址、大小是时刻变化的,所以采用链表来寻找空闲块和合并空闲块。正是因为链表,且链表的内存块大小是不定的,从而内存分配和回收的时间无法确定。
    (2)带固定大小特性的可变大小的存储管理。固定大小,是指大小只能为2的k次幂,又可以分配2i大小的内存,如伙伴系统。

嵌入式系统对内存管理的特殊要求

  1. 内存能快速申请和释放,嵌入式系统的实时性保证要求内存分配过程要尽可能地快,这要求算法简单。
  2. 内存应该各尽其用,要尽可能减少浪费。
    以上两点存在矛盾。

aCoral的内存管理机制

在伙伴系统基础上,采用位图法方式提高内存分配和回收的速度,更能满足系统实时性的需求。
首先,aCoral内存管理分为两级,上一级采用改进的伙伴系统,负责要分配的内存大小,下一级根据上一级确定的大小进行具体物理内存分配。
第一级内存管理总会分配2N大小的内存,第二级采用了固定块和可变大小两种内存管理方式,除内核外,应用程序一般直接使用第一级的伙伴系统。

第一级内存管理算法

伙伴算法及实现上的改进:如果整个可用空间内存由2m个字节组成,那么在系统中对每个大小为2n的内存块建立一个对应的可用块链表。
假设内存地址从(0~2m-1),刚开始,整个2m个字节空间都是可用的。
假如应用程序申请2K个字节的内存空间,如果系统中没有2K大小的内存块时,就把更大的可用内存块分成两部分,最终得到两个大小为2K的内存空间。
当一块分为两块时,这两块就成为彼此的伙伴。
某个时刻两个伙伴都空闲时,又可以合并成一个大的空闲块。
该方式的分配和回收速度快,算法简单。
但此算法存在一些缺点:

  1. 尽管大小为2K内存块回收时只需要搜索2K字节大小的块来判断是否需要合并,但是时间无法确定,因为链表只能顺序搜索。
  2. 内存控制块的组织问题。一般的实现方式是在内存块的开始部分预留出一段内容来作为内存控制块。
    在这里插入图片描述

这样做有一个缺点,若用户分配512B的内存块,内存管理系统经过计算后知道需要的内存块其实为512+控制块的大小(10B)= 522B,因此系统会分配一个1024的内存块。
内存控制块本身也属于内存块的一部分,把控制块单独抽出来,而真正可以的内存块留给用户就可以解决该问题,这就是改进的伙伴系统。
在这里插入图片描述

aCoral伙伴算法的实现思路

首先真正的物理内存被分成了两部分。

  • 一部分被内存控制结构使用,内存初始化函数buddy_init()将逐个初始化这些结构。
  • 剩下的内存是用户可用内存。这些内存被划分为众多基本块,每个基本块的大小可以通过BLOCK_SIZE设置(默认1<<7字节),这样内存的分配和回收都是基于序列的了,换句话说,这些基本内存块是从0到n逐个标记的。在逻辑上,这些内存块被组成了m层,最大层数m层可以通过LEVEL配置(默认14)。

第0层每个内存块的大小为BLOCK_SIZE,第1层每个内存块的大小为2*BLOCK_SIZE,第n层内存块的大小为BLOCK_SIZE<<n。(BLOCK_SIZE x 2n)。

  1. 当需要分配2i个字节大小的内存块时,用公式:log(2i/BLOCK_SIZE)可求出应该从哪一个逻辑层分配内存。
  2. 如果该层有空闲块,即可分配。
  3. 当一这层没有空闲块时,就向上层申请,最终会得到两个空闲的2i字节大小的内存块。

这里需要注意的是为了最终得到对应的物理内存块,这些逻辑的内存块始终是有序号标记的。

以8个基本内存块大小的内存来举例,开始的时候8个基本内存块全空闲(其序号分别是0,…,7),内存初始化时可能将这8个基本内存块注册第3层(Buddy_init(),第0层的基本内存块可能都不能直接使用,用户只感觉到第3层的内存块(该层的内存块大小就是8个基本内存块大小(BLOCKSIZE<<3)))。
此时,若系统申请一个BLOCK_SIZE大小的内存块,根据公式可得到:应该从第0层分配,而这时除了第3层,其余层的内存块都不可用(内存初始化时设定)。那么第0层向第1层申请,第1层向第2层申请,直到第3层。
第3层将唯一的一个内存块分成两个,供第2层使用,第2层取出一个(通常是序号小的)分配给第1层,另外一个标记为空闲。依次向下,0层有了两个空闲块,即基本内存块0、1。
根据基本内存块的序号(0)转换成相应的物理地址返回给调用函数。
内存回收的时候,传入的参数是地址,先把地址转换成序号,再做回收。
回收的同时如果发现伙伴也是空闲,则向上合并成一个大的空闲块,从而减少外碎片。

为了标记每个逻辑内存块的空闲状态和快速找到一个空闲块,每层需要一个内存状态位图块、空闲内存块链表数组、空闲内存块链表头三个结构。
同时,为了回收的效率,还需要为每个基本内存块存储逻辑层信息,即原来从哪一个逻辑层分配。

/*内存控制块*/
typedef struct{
	acoral_32 *free_list[LEVEL]; //空闲内存块链表数组
	acoral_u32 *bitmap[LEVEL]; //内存状态位图块
	acoral_32 free_cur[LEVEL]; //该层空闲内存块链表头
	acoral_u32 num[LEVEL]; //各层基本内存块(BLOCK_SIZE)个数
	acoral_8 level; //伙伴系统的层数
	acoral_u32 start_adr; //伙伴系统管理的内存的起始地址
	acoral_u32 end_adr; //伙伴系统管理的内存的末尾地址
	acoral_u32 block_num; //基本内存块的数量,等于num[0]
	acoral_u32 free_num; //剩余的基本内存块的数量
	acoral_u32 block_size; // 基本内存块的大小
	acoral_spinlock_t lock; //自旋锁
}acoral_block_ctr_t;

内存控制块acoral_block_ctr_t是aCoral进行内存分配和回收过程的关键数据结构,其中的一个重要成员“acoral_u32 *bitmap[LEVEL]”是描述内存块状态的状态位图数组。
bitmap实际上是一个二维数组bitmap[m][n],第一个下标代表位图块所在的层数m,第二个下标代表该层的第n个位图块。
状态位图块数组bitmap[m][n]的每个值是32位,而每一位代表相邻两块内存块,所以每个值可以表示64个内存块的分配情况。
0~(m-1)层中,相邻的两块内存块由空闲位图块中的一位来标识是否空闲,对于第i层,每个空闲块由空闲内存块中的一位来标识是否空闲。
1位只有0和1两种状态,而两块内存块有四种状态:两块都空闲、没有空闲块、只有奇数空闲块、只有偶数空闲块,而正常情况下不存在两块都空闲的状态,因为会合并,所以0~(m-1)层的相邻的两块内存块只有两种状态:没有空闲块,有一块是空闲的。
在这里插入图片描述

虽然通过状态位图块解决了回收时复杂度O(n)的问题,但是没有解决空闲内存块分配问题,即分配内存时如何查找某一层空闲的内存块。
所以通过增加空闲内存块链表数组“acoral_32 *free_list[LEVEL]”来实现分配时O(1)复杂度,同时还可以解决内存控制块占用部分内存块导致的问题。

首先定义了空闲位图块链表头数组“acoral_32 *free_cur[LEVEL]”,该数组元素指向第一个空闲位图块的标号,空闲内存块链表数组free_list[LEVEL]的值指出了下一个空闲位图块。

对于第0层,free_cur[0]等于2,那么读取free_list[0][2]得到下一个空闲位图块,假设其值为4;则读取free_list[0][4],再得到下一个空闲位图块,依次往后,形成一个表示内存状态位图块的表链,这里的2和4表示第0层内存状态位图块的标号,2表示此时标号为2的内存状态位图块中的32位有非0位。

根据前面的描述,只要根据第m层的free_cur[m]找出空闲位图块的标号i,然后读取内存状态位图块bitmap[m][i],再判断首先出现“1”的那一位,并找到该位对应的内存块序号,便可确定该内存块对应的基本内存块标号,最后得到相应的物理地址,返回给用户,并将刚才的“1”设置为“0”,如果此时该内存状态位图块变为0,则更改free_cur[m]=free_lisy[m][i]。

系统回收内存块时,传送的是地址,根据地址可以知道这个内存地址开始地址对应的基本内存块的标号。但如何知道内存块对应的大小呢?不同层分配出去的内存块的起始地址可能一样,这就需要一个数据结构来保存基本内存块i的起始地址所对应的逻辑内存大小,aCoral用最小内存控制块acoral_block_t来保存某个基本内存块的分配情况。

由于标号为奇数的基本内存块肯定是从第0层分配出去的,偶数的基本内存块由非零层分配出去。因此,不用保存其是从第几层分配出去的。
如果基本块的值尚未被分出去,则level的值为-1。
level的值同时也可用来区分内存块位图管理时的两块兄弟内存块的状态,根据前面的描述1~m-1层,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/155999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pinely Round 1 (Div. 1 + Div. 2) A. Two Permutations

来写一道*800的题&#xff0c;思路很简单&#xff0c;就是这道题我们应该怎么去严谨地思考Problem - 1761A - Codeforces思路&#xff1a;结论题的分类讨论一定要不重不漏一开始很容易想到&#xff0c;前缀和后缀不能有重合那么有重合部分就判No没有重合的情况&#xff1a;隔1个…

Linux常用命令——xz命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) xz POSIX 平台开发具有高压缩率的工具。 补充说明 xz命令XZ Utils 是为 POSIX 平台开发具有高压缩率的工具。它使用 LZMA2 压缩算法&#xff0c;生成的压缩文件比 POSIX 平台传统使用的 gzip、bzip2 生成的压缩…

多线程进阶(二)Callable接口,JUC下常见类使用及线程安全集合类

目录 前言&#xff1a; Callable接口 代码实现 JUC下常见类使用 ReentrantLock类 代码实现 信号量 代码实现 CountDownLatch类 代码实现 线程安全的集合类 多线程环境下使用ArrayList 多线程环境下使用队列 多线程环境下使用哈希表 小结&#xff1a; 前言&#…

图解24种经典k线图

相信不少国内的老股民心中都藏着一份经典的K线图图解&#xff0c;当中也许有6种、12种或24种能揭示行情方向转变的“K线脸谱”&#xff0c;借由它们的对行情的预知作用&#xff0c;股民度过一次又一次的熊牛更替。其实只要善于变通&#xff0c;它们同样适用于贵金属投资&#x…

再说多线程(一)

世界是并行&#xff01;做过复杂项目的朋友一定遇到过并发的问题&#xff0c;无论是大项目如订票系统&#xff0c;还是小项目中的文件管理都会有并行需求。所以不同于上学时接触的大部分代码&#xff0c;实际的业务往往是为多人提供服务&#xff0c;必然天然的带有并发的需求。…

Mybatis-plus 使用 typeHandler 将 String 拼接字符串转换为 List 列表

一、需求描述 首先说明需求&#xff0c;有三张表&#xff1a; 学生表、角色表、以及一张关联的中间表。 学生可以有多个角色&#xff0c;但是这多个角色我是作为多条记录存储在另外一张表中的&#xff0c;现在想将这多条记录查询出来&#xff0c;注入到Student对象中的一个L…

微服务之JVM调优

一、Xms Xmx Xss等定义及功能 1.Xms 是指设定程序启动时占用内存大小。一般来讲,大点,程序会启动的快一点,但是也可能会导致机器暂时间变慢。 2.Xmx 是指设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异…

易基因|14种全基因组DNA甲基化测序(WGBS)标准分析比对软件的比较| 生信专区

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。全基因组重亚硫酸盐测序&#xff08;WGBS&#xff09;是甲基化研究的重要技术。尽管已经开发了一系列工具来解决由亚硫酸盐处理引起的比对问题&#xff0c;但尚未对最新可用工具的reads比对…

HashMap,Hashtable,ConcurrentHashMap

目录 一、多线程使用HashMap的一些线程安全问题 ①造成数据新增丢失 ②扩容时候&#xff0c;造成链表成环 二、Hashtable和HashMap的区别 ①核心方法加锁 ②其他语法上面的略微差异 三、引入ConcurrentHashMap【重要】 ①ConcurrentHashMap相比于Hashtable的优势 Hashtab…

著名相声艺术家侯耀华,77岁寿宴现场曝光,郭德纲师哥前去祝贺

在中国的相声界&#xff0c;有一条不成文的规定&#xff0c;关于著名相声表演艺术家的判定&#xff0c;从来不是以相声水平高低为标准。只要你有足够长的寿命&#xff0c;只要你能把其他人都熬走熬败&#xff0c;就算你是一个相声小白&#xff0c;也能摇身一变成为艺术家。 不过…

Git介绍与使用

1.集中式版本控制 svn 中央服务器 所有的版本数据都存在服务器上&#xff0c;用户本地只有自己所同步的版本&#xff0c;如果不联网的话&#xff0c;用户就看不到 SVN是集中式版本控制系统&#xff0c;版本库是集中放在中央服务器的 而工作的时候,用的都是自己的电脑,所以首先…

跨境电商物流系统功能框架

随着国内互联网巨头们逐渐将更多注意力投向了跨境电商市场&#xff0c;电商巨头出海也在掀起新的发展高潮。下面是跨境电商物流系统功能框架&#xff0c;供大家参考1、OMS叫做订单管理系统&#xff08;Order Management System&#xff09;&#xff0c;在不同公司&#xff0c;不…

云原生时代的运维体系进化

云原生已经成为数字经济技术的创新基石&#xff0c;并且正在深刻地改变企业上云和用云的方式。云原生的用云方式可以帮助企业最大化获得云价值&#xff0c;也给企业的计算基础设施、应用架构、组织文化和研发流程带来新一轮变革。而业务和技术挑战也催生了新一代云原生运维技术…

设计模式(一)----设计模式概述及UML图解析

1、设计模式概述 1.1 软件设计模式的产生背景 "设计模式"最初并不是出现在软件设计中&#xff0c;而是被用于建筑领域的设计中。 1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫亚历山大&#xff08;Christopher Alexander&#xf…

Golang开发 02

文章目录一、Golang开发工具二、visual studio code安装(VS code)1、安装window2、安装mac、linux一、Golang开发工具 # 1、Visual studio code &#xff08;常用&#xff09; # 2、Sublime Text(免费) # 3、Vim # 4、Emacs # 5、Eclipes IDE工具&#xff0c;开源免费&#xf…

数据分析-深度学习PytorchDay1

深度学习框架pytorch学习(一)准备环境准备环境一、深度学习框架简介二、Tensorflow与Pytorch的比较三、安装开发环境一、深度学习框架简介1、Google阵营最早的是由加拿大团队开发的theano一个机器学习库&#xff0c;现在已经停止更新。接着Google开发了Tensorflow&#xff0c;并…

【机器学习知识点】3. 目标检测任务中如何在图片上的目标位置绘制边界框

目录前言导入图片定义边界框绘制函数在图片中绘制边界框总结前言 在图像分类任务中&#xff0c;很多时候我们不仅要知道图像中目标的类别&#xff0c;而且还想知道它们在图像中的具体位置。在计算机视觉里&#xff0c;这类任务被称为目标检测&#xff08;object detection&…

uniapp开发技术

目录 1、js 判断iPhone|iPad|iPod|iOS|Android客户端 2、js实现防抖 3、 js实现节流 4、 页面在弹窗时禁止底部页面滚动&#xff08;h5端&#xff09; touchmove.stop.prevent 5、scrollIntoView 1、js 判断iPhone|iPad|iPod|iOS|Android客户端 // fullScreen代表整个页面…

【C++】STL---list的模拟实现

目录前言一、list和vector的区别二、节点的定义三、list类定义四、push_back函数五、push_front函数六、迭代器七、begin和end函数八、迭代器区间初始化九、迭代器的操作符重载操作符重载操作符- -重载操作符&#xff01;重载操作符重载操作符*重载十、insert函数十一、erase函…

如何应用卫星图像插入到Auto CAD

如何应用卫星图像插入到Auto CAD发布时间&#xff1a;2018-01-17 版权&#xff1a;工具准备BIGEMAP GIS Office&#xff1a;http://www.bigemap.com/reader/download/案例&#xff1a;等高线完美套合卫星影像教程本实例使用AutoCAD2008软件进行影像与矢量数据叠加配准。影像获取…