首页 > 系统相关 >关于ucosii操作系统原理------(三)内存管理

关于ucosii操作系统原理------(三)内存管理

时间:2024-06-05 12:30:29浏览次数:26  
标签:控制 ucosii pblk 分区 链表 内存 ------ 指针

目录

一、引言

二、内存控制块

三、内存管理的实现源码

1.动态内存分区创建函数

2.获得一个内存块函数

3.释放内存块函数


一、引言

     接下来说一下实时操作系统的动态内存管理。对于实时操作系统来说,动态内存分配的执行时间必须是可确定的。ucosii对malloc()函数和free()函数这两个动态内存分配和释放内存函数进行了改进,使得它们可以对大小固定的内存块进行操作,使得其执行时间可以确定,满足实时要求。

二、内存控制块

      先来说一下内存分区和内存块的概念;操作系统以分区为单位来管理动态内存,而任务以内存块为单位来管理内存。而内存分区和内存块的使用情况是由内存控制块来进行记录的,内存分区和内存块以及内存控制块的关系图如下所示:

     从上图我们可以看到,内存分区是包含内存块的,多个大小相等的内存块组成内存分区。因此OS定义了一个U16的二维数组Intmenbuff[10][10]来存放内存分区,这个数组表示有10个内存块,每一个内存块的长度为10,10个内存块构成分区。同TCB和ECB一样,为了方便管理内存分区,OS也定义了一个内存控制块OS_MEM,接下来我们看看这个结构体里边的内容:

    从上图可以看出OSmemFreeList用于充当链表指针,这也代表着内存控制块也和TCB类似,构成了一个内存控制块链表。每当系统需要创建一个内存分区时,就会从内存控制块链表摘取一个内存控制块(这里很重要也就是说先从内存控制块链表中取出一个内存控制块来充当内存分区,如果这里不理解,对接下来的分区创建函数源码里边定义了一个二维指针会很茫然),当用完这个内存控制块释放时,又将这个内存控制块归还内存控制块链表,用于新的内存分配。同样的,这一步链接成链表的操作也是在系统调用OSInit()函数时候完成的,也是定义了一个数组OSMemTb1[]数组来存放链表的各个节点,因为在OSInit()函数内部会调用OSMemInit()函数,该函数用于将OSMemTb1[]数组的各个元素链接成链表,并且各个节点进行初始化;接下来看看这个函数的源码:

   上述源码都是链表的简单操作,学过链表的都知道,链表中的节点存放着本身的元素和下一个节点的地址,因此,这里也是将每个数组的元素的节点链接起来。首先利用OS_MemClr函数将OSMemTb1[]数组里的内容全部清空,也相当于是都初始化成0;然后根据内存分区中内存块数目的多少去判断有多少节点,并根据节点数进行链表的连接,也就是将第二个节点的地址赋值给第一个元素的链表指针。可以看到这里是用了一个for循环,循环各个节点(也就是OSMemTb1数组的各个元素),用一个指针变量去获取头指针,然后依次将下一元素的地址赋给前一个节点。接下来pmem=&OSMemTb1[i]代表的是链表中的最后一个元素的地址,接下来将链表的最后一个节点的链表指针赋值NULL代表链表结束。最后将链表的首元素赋给头指针,用作链表的起始。

三、内存管理的实现源码

1.动态内存分区创建函数

       老规矩,我们来看一下内存创建函数OSMemCreate()的源码,先看一下这个函数里边的各个参数;

        其关键源码如下:

        上述代码是去判断各个参数是否满足要求,首先去判断内存分区的起始地址是否为空,如果为空,返回错误信息,创建内存分区失败。再去判断addr指针变量是否按照指针大小对齐(对齐的目的是为了更快的访问内存数据),也即是if(addr&(sizeof(void *)-1))这行代码。接下来对其做详细分析,首先什么是对齐?对齐代表的是数据在内存中的起始地址必须是某个特定值的倍数。接下来看到sizeof(void *),sizeof是一个操作符,返回一个数据类型或者变量类型的大小,以字节为单位,这里参数是void*,代表返回的是指针类型的大小,而在32位系统中,指针占4个字节,而在8位系统中,指针占8个字节。而我们一般用的是32单片机,因此这里实际上就是去判断这个addr指针变量是否是4字节对齐的?也就是说只要addr这个内存分区起始地址是4的倍数,则对齐。而只要地址是关于4字节对齐的,它的最后两位地址是00,因此只需要将addr与sizeof(void *)-1=3(0x03)按位与,就可以知道addr是否对齐。紧接着判断内存块的数目是否大于两块,必须要求内存块的数目两块以上,否则创建失败。最后再去判断每一个内存块的长度是否在4字节以上。   

      在做完一系列的条件判断后,接下来我们看上述源码,主要做了三件事情,第一是从内存控制块链表中摘取一个内存控制块来作为内存分区;第二是将这个内存分区中的各个内存块链接成一个链表;第三是填充这个内存分区的基本信息。我们来看一下每一件事情具体是怎么做的。首先是定义一个指针变量pmem用来存放空内存控制块的链表指针OSMemFreeList,表示摘取一个空内存控制块当作内存分区,然后去判断这个内存控制块链表指针是否为0,也就是说链表是否结束,还有没有下一个内存控制块,如果不为空,那么就更新空内存控制块链表指针,使其指向下一个空内存控制块。最后再去检测是否成功获得内存分区。

      接下来我们来看第二件事情是怎么做的;首先定义了一个二维指针变量plink,这是为什么要这样定义呢?刚刚在上边我已经提到了,OSMemFreeList是一个内存控制块链表指针,指向的是一个空内存控制块,然后将这个空的内存控制块作为一个内存分区。那么既然是内存分区,而内存分区由多个内存块组成,那么我们就需要将这些内存块构建成链表链接起来,那需要链接,自然也就需要链表指针;而内存分区就是一个指针,我们还要在内存分区里边定义一个链表指针,因此就变成了指向指针的指针了。明白了这一点,接下来就很好解释了,和前文提到的链表基本链接操作一样,首先是将plink初始化为*addr代表的是头指针,然后把pblk定义为addr表示的是当前内存块的地址。然后我们进入for循环,以内存块数目进行遍历,pblk+=blksize;这行代码表示的是将pblk指向为下一个内存块的地址再赋给pblk,因为每一个内存块都是由长度的,blksize代表的是每一个内存块的长度。*plink=(void *)pblk;这行代码代表的是将当前内存块的指针指向下一个内存块;而plink=(void **)pblk;代表的是把plink更新为下一个内存块的地址;此处举一个例子用以说明:

假设我们有一个内存区域,起始地址为 0x1000,每个内存块大小为 4 字节,包含一个指针大小。我们有三个内存块。地址如下:

- `0x1000`: 第一块
- `0x1004`: 第二块
- `0x1008`: 第三块

**链表创建过程**:

1. 初始化:
    - `plink = 0x1000`
    - `pblk = 0x1000`
    - `loops = 2`(三个块减去一)

2. 第一次循环:
    - `pblk += 4` -> `pblk = 0x1004`
    - `*plink = 0x1004` -> 在 `0x1000` 位置写入 `0x1004`,表示第一个块指向第二个块,相当于是在当前节点写入下一个节点的地址,
    - `plink = 0x1004`//将链表指针更新为下一节点的地址,用于下一次循环

3. 第二次循环:
    - `pblk += 4` -> `pblk = 0x1008`
    - `*plink = 0x1008` -> 在 `0x1004` 位置写入 `0x1008`,表示第二个块指向第三个块
    - `plink = 0x1008`

4. 结束:
    - `*plink = 0` -> 在 `0x1008` 位置写入 `0`,表示第三个块指向 `NULL`。

通过这种方式,链表就创建完成了,`0x1000` -> `0x1004` -> `0x1008` -> `NULL`。

   最后,第三件事情登记信息完成以后,返回pmem这个内存分区指针。

2.获得一个内存块函数

        可以调用OSMemGet(OSMem *pmem,u8 *err)函数来向某内存分区获得一个内存控制块,函数返回所请求成功的内存控制块指针。接下来看这个函数的关键源码:

        从上述源码我们可以看到,首先会检查这个内存分区里边是否有空余的内存块数,OSMemNFree文章在一开始就提到代表的是分区内剩余的可分配内存块数。如果有剩余,那么这个内存分区内的内存控制块链表的一个内存控制块地址赋给指针变量pblk,然后更新当前的链表指针指向下一个内存控制块。接下来将剩余的可分配内存块数目减一,代表已经分配出去一个内存控制块了,最后返回这个内存控制块的地址。

3.释放内存块函数

         可以调用OSMemPut(OSMem *pmem,void *pblk)函数来释放一个内存块,函数中的第一个参数代表的是需要释放的内存块的内存分区指针,第二个参数代表的是需要释放的内存块指针。其源码如下:

   从上述源码可以看到,先去判断当前内存池里边可分配的剩余内存块数目是否大于等于内存池里边本身的内存块数目,如果是的话,代表这个内存池满了,不需要再释放内存块,返回一个错误信息。如果内存池未满的话,将要释放的内存块指针pblk转换为void **类型,再将其指向内存控制块链表的头指针,相当于是在链表的头部插入一个指针作为新头指针。接下来将这个插入的头指针作为新的链表头指针;最后将分区的可分配内存块数自增代表已释放内存控制块,最后返回一个成功的指示信息。

标签:控制,ucosii,pblk,分区,链表,内存,------,指针
From: https://blog.csdn.net/weixin_52247452/article/details/139388799

相关文章

  • word怎么改成图片?四个专业的方法,批量把word转为图片
    传统的Word文档在分享时可能受到格式、字体、排版等多种因素的限制,导致接收方无法完全还原原文档的样式。而通过将Word文档转换为图片格式,可以确保文档内容的完整性和一致性,使接收方能够准确理解文档内容。此外,图片格式的文件更容易在社交媒体、论坛、博客等平台上进行分享和传......
  • Trie字典树和AC自动机 (题目&答案)
    A.三年二班的投票题目描述三年级二班已经完成了竞选班长的投票,已知一共有n张投票,每张投票上写了一位同学的名字。投票统计结束后,张老师随意问一个同学的名字,请编程快速检索出,该同学共有几票。输入第一行读入一个整数n,代表产生了n张投票。(n≤)接下来n行,每行有一......
  • 如何一键提取文件夹下所有文件名?亲测好用的四个方法
    在数字化时代,文件管理和整理成为我们日常工作中不可或缺的一部分。尤其是在处理大量文件时,如何快速有效地提取文件夹下所有文件的名称,成为提高工作效率的关键。本文将详细介绍如何一键提取文件夹下所有文件名,帮助读者轻松应对文件整理的需求。如何一键提取文件夹下所有文件名?......
  • 批量修改图片宽高的软件有哪些?细数几个图片处理软件
    批量修改图片的宽高,通常是在需要处理大量图片并且要求这些图片保持统一尺寸或适应特定尺寸需求时进行的操作。这种需求在多个领域和场合中屡见不鲜,例如,在网页设计和制作中,批量修改图片宽高是非常必要的步骤。为了保持网页的美观和整洁,设计师通常会将网页上的图片尺寸调整到统一......
  • 商淘云助力连锁门店同城引流、会员营销及连锁品牌节税整体解决方案
    随着市场竞争的日益激烈,连锁门店如何在同城范围内迅速提升品牌知名度、吸引并维护忠诚顾客群体,同时确保税务合规以降低经营成本,成为了摆在连锁品牌面前的重要课题。商淘云,作为一家专注于智慧零售解决方案的科技企业,凭借其深厚的行业经验和先进的技术实力,为连锁门店提供了一套同......
  • PyQT5之QComboBox
    importos.pathfromPyQt5importQtWidgetsfromPyQt5importQtCore,QtGuiimportsysimportcv2classSpinBoxPanel(QtWidgets.QWidget):def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)select_btn=QtWidgets.QP......
  • static vs Singleton,静态类vs单例模式之争
    https://stackoverflow.com/questions/519520/difference-between-static-class-and-singleton-pattern?answertab=modifieddesc#tab-top单例模式可以用接口,Singletoncanimplementinterface可以通过单例类来实现接口,但不能通过类的静态方法或者在某些语言(如C#)中的静态类来......
  • 项目整合管理主要输入、工具、输出
    一、制定项目章程:编写一份正式批准并授权项目经理使用组织资源、进行项目规划、执行和控制的文件。作用:1.明确项目与组织战略目标间的直接联系2.确立项目的正式地位3.展示组织对项目的承诺1.1输入:1.立项管理文件:业务视角描述必要性,决定是否值得投资,包括商业需求和成本效益......
  • Mock 工具使用 - 模拟弱网测试
    在当今移动互联网的时代,网络的形态非常多变,不光有2G,3G,4G,不同的制式、不同的速率,让我们移动应用运行的场景更加丰富。而且移动产品使用场景非常多变,如近地铁,上公交,进电梯,进山区等是的弱网测试显得尤为重要。对于弱网的数据定义,不同的应用所界定的含义不完全一样。不仅要考虑各......
  • c++ 运算符重载、线程安全实现单例
    一、运算符重载namespaceCalcRect{ structRect { Rect(intposX=0,intposY=0,intw=0,inth=0) { x=posX; y=posY; width=w; height=h; } voidoperator=(constRect&other) { x=other.x; y=other.y; width=ot......