从零开始学习UCOSII操作系统12–内存管理

从零开始学习UCOSII操作系统12–内存管理从零开始学习UCOSII操作系统12–内存管理前言:在标准的C语言中,可以用malloc()和free()2个动态的分配内存和释放内存,但是在嵌入式中,调用malloc()和free()却是非常危险的。因为多次调用这两个函数,会把原来的很大的一块连续的内存区域逐渐的分割成许多非常小的而且彼此又不相邻的内存块,也就是所谓的内存碎片。这样子的话,使得程序后面连一段非常小的内存都分

大家好,又见面了,我是你们的朋友全栈君。

从零开始学习UCOSII操作系统12–内存管理

前言:
在标准的C语言中,可以用malloc()和free()2个动态的分配内存和
释放内存,但是在嵌入式中,调用malloc()和free()却是非常危险的。
因为多次调用这两个函数,会把原来的很大的一块连续的内存区域逐渐的分割成许多非常小的而且彼此又不相邻的内存块,也就是所谓的内存碎片。这样子的话,使得程序后面连一段非常小的内存都分配不到,另外由于内存管理算法上的原因,malloc()和free()函数执行的时间是不确定的。

1、分区的概念:

操作系统把连续的大块内存按分区来管理,每个分区中包含整数个大小相同的内存块,利用这种机制,UCOSII对malloc和free函数进行了改进。使得他们可以得到和释放固定的大小的内存块。这样子malloc和free函数的执行时间就是确定的了。(为什么?)

函数原型:void * malloc(unsigned int num_bytes);
//分配长度为NUM_BYTES字节的内存块。

返回值是void指针,void*表示未确定的类型的指针,明确说明,这个函数仅仅只是为了申请内存的空间,而不是申请特定内存空间。

所以用户可以根据需要把malloc申请到的内存,强制转换成自己需要的那种模式。但是有一个缺点:malloc只管分配内存空间,并不对空间进行初始化的操作,所以申请到的内存的值是随机分配的,经常会使用memset()进行置0的操作后再重新的使用。

(3)一个简单的调用函数的实例:
int p;
p = (int 
)malloc(sizeof(int) 128);
//这里需要检查一下,是否分配成功了,分配不成功需要上报一个错误的值
double 
pd = (double )malloc(sizeof(double) 12);

free(p);
free(pd);

p =NULL;
pd = NULL;

指针赋值完后,需要赋值为NULL是一个良好的习惯。

2、内存控制块

为了便于内存的管理,在UCOSII中使用内存控制块的数据结构跟踪每一个内存分区。系统中的每个内存分区都有它自己的内存控制块。

OSMemAddr:
指向内存分区的起始地址的指针,她在建立内存分区初始化的时候,在此之后就不能更改了。

OSMemFreeList:
指向下一个空余内存控制块或者下一个空余内存块的指针,具体的含义应该要根据内存分区是否已经建立来决定。

OSMemBlkSize:
内存分区中内存块的大小,是建立该内存分区时定义的。

OSMemNBlks:
内存分区中总的内存块的数量,也是建立该内存分区时定义的。

OSMemNFree:
内存分区中当前可以获得的空余的内存块的数量。

typedef struct 
{
    void * OSMemAddr;
    void * OSMemFreeList;
    INT32U OSMemBlkSize;
    INT32U OSMemBlks;
    INT32U OSMemNFree;
}OS_MEM;

3、使用内存管理机制:

建立一个内存分区:OSMEMCreate()

在使用一个内存分区之前,必须先建立该内存分区,这个操作可以通过调用函数OSMemCreate来完成,下面是一个创建100个内存块并且每个内存块大小为32B的内存分区。

OS_MEM  * CommTxBuf;

//在创建一块内存分区之前,需要在栈中建立申请一块内存。
INT8U  CommTxPart[100][32];

void main(void)
{
    INT8U err;
    OSInit();

    CommTxbuf = OSMemCreate(CommTxPart,100,32,&err);

    OSStart();

}

申请一块内存分区OSMemCreate()
OS_MEM OSMemCreate(void addr, INT32U nblks,INT32U blksize,INT8U err)
{

/
初始化定义一些 变量/
OS_MEM 
pmem;
INT8U pblk;
void *
plink;
INT32U i;

pmem = OSMemFreeList;
if(OSMemFreeList != (OS_MEM *)0)
{
    OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}

}

分配一个内存块OSMemGet()
应用程序可以调用OSMemGet()函数,从已经建立的内存分区中申请一个内存块,该函数的唯一的参数就是指向特定内存分区的指针。

void *OSMemGet(OS_MEM * pmem, INT8U *err)
{
    void *pblk;
    OS_ENTER_CRITICAL();

/* 如果内存空闲块的大小大于0的话    */
    if(pmem->OSMemNFree > 0)
    {
        pblk = pmem->OSMemFreeList;
        pmem->OSMemFreeList = *(void **)pblk;
        pmem->OSMemNFree--;
        OS_EXIT_CRITICAL();
        *err = OS_NO_ERR;
        return (pblk);
    }

    OS_EXIT_CRITICAL();
    *err = OS_MEM_NO_FREE_BLKS;
    return ((void *)0);
}

PS:可以在中断服务子程序中调用OSMemGet(),因为在暂时没有内存块可用的情况下,OSMemGet()不会等待,而是立即返回NULL指针。

当然有请求一个内存块,就会有一个释放一块内存块:

OSMemPut()
当一个应用程序不再使用某个内存块时,必须及时的把它释放,病放回相应的内存分区中,这个操作由OSMEMPut函数完成。

必须注意的是OSMemPut()并不知道该内存块时属于哪个内存分区的,也就是说,如果用户程序从一个包含32B内存块的分区中分配了一块内存块,那么用完之后,千万不能返还一个包含120B内存块的内存分区,因为,当应用程序下一次申请120B分区中的一个内存块中,它只会得到32B的可用空间。
其他的88B属于其他的任务,这就有可能使得系统崩溃。

OSMemPut()

第一个参数是指向:OSMemput()第一个参数pmem是指向内存控制块的。
pblk指向由pmem管理的内存块。

OSMemPut()函数必须确保传递给它的参数指针非空的,但是遗憾的是,OSMemPut()并不知道要释放的内存块是否属于此内存分区,因此,应用程序必须保证把内存块释放到合理的内存分区中。

INT8U OSMemPut(OS_MEM * pmem, void * pblk)
{

    *(void **)pblk = pmem->OSMemFreeList;
    pmem->OSMemFreeList = pblk;
    pmem->OSMemNFree++;
    OS_EXIT_CRITICAL();
    return (OS_NO_ERR);
}

4、查询一个内存分区的状态,OSMemQuery()

在UCOSII中可以使用OSMemQuery函数来查询一个特定内存分区的有关信息。通过该函数尅知道特定内存分区中内存块的大小,可用内存块的数目,和已经使用了的内存块数目等信息。

所有的信息都存放在一个叫做OS_MEM_DATA数据结构中。

typedef struct 
{
    void *OSAddr;      //指向内存分区首地址的指针
    void *OSFreeList;  //指向空余内存块链表首地址的指针
    INT32U  OSBlkSize; //每个内存块所含有的字节数
    INT32U  OSNBlks;   //内存分区总的内存数
    INT32U  OSNFree;   //空余内存块的总数
    INT32U  OSNUsed;   //正在使用的内存块总数
}OS_MEM_DATA;

5、使用内存分区,理解这个小实例的应用

UCOSII中的动态内存分配功能并利用它进行消息传递。
第一个任务读取并检查模拟量输入的值(如气压、温度以及电压等)如果该值超过一定的阈值,就向第二个任务发送一条消息,该消息中含有出错的信息,出错的通道号以及其他的出错的代码。

AnalogInputTask()
{
    for(;;)
    {
        for(所有模拟量的输入)   //不断的轮训来查询任务一的模拟量
        读取模拟量的输入值;
        if(模拟量超过阈值)
        {
             得到一个内存块,
                得到当前系统的时间
                将刚刚错误的几项存入到内存块里面。
        }
    }
    延时任务,直到再次对模拟量进行采样时为止。
}


ErrorHandlerTask()
{
    for(;;)
    {
        等待错误处理队列的消息
          得到指向包含有关错误数据的内存块的指针
        读入消息,并根据消息的内存执行相应的操作。
        将内存块放回到相应的内存分区中
    }
}

6、等待内存分区中的一个内存块

有时候,在内存分区暂时没有可用的空余内存块的情况下,让一个申请内存块的任务等待也是可以的,但是UCOSII本身在内存管理上面并不支持这项功能,如果确实需要,则可以通过为特定内存分区增加计数型信号量的方法,实现这个功能。

(2)应用程序为了申请分配内存块,首先得到相应的信号量,然后才能调用OSMemget()函数,如果需要释放内存块,只要将内存块释放到相应的内存分区中,并且发送一个信号量即可。

(3)显然如果系统中只有一个任务使用动态内存块的话,就没有必要使用信号量了,因为这种情况没有必要使用信号量了,这种情况不需要保证内存资源的互斥,事实上,除非要实现多任务的共享内存,否则连内存分区都不需要。

(4)当一个任务运行的时候,只有在信号量有效的时候,才能得到内存块,一旦信号量有效了,就可以申请内存块并且使用它,儿没有必要对OSSemPend()返回的错误代码进行检查。

因为只有在这里,只有当一个内存块被其他的任务释放病返回到内存分区的时候,UCOSII才会返回到该任务中,使得继续运行。

书中的小实例:

OS_EVENT  * semaphorePtr;
OS_MEM    * PartitionPtr;
INT8U     partition[100][32];
OS_STK    TaskStk[1000];

void main()
{
    INT8U  err;
    OSInit();

    SemaphorePtr = OSSemCreate(100);
    PartitionPtr = OSMemCreate(Partition,100,32,&err);

    OSTaskCreate(Task,(void *)0, &TaskStk[999], &err);

    OSStart();
}

void Task(void * pada)
{
    INT8U err;
    INT8U *pblock;

    for(;;)
    {
        //这里使用了我请求一个信号后,然后我才释放掉一个信号量
        OSSemPend(Semaphore,0,&err);
        pblock = OSMemGet(PartitionPtr, &err);

        //使用内存块,具体怎么用,按照客户的要求
        OSMemPut(PartitionPtr, pblock);
        OSSemPost(SemaphorePtr);
    }
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/136076.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月4日 上午10:20
下一篇 2022年5月4日 上午10:20


相关推荐

  • Webgame 设计与开发之内容简介

    Webgame 设计与开发之内容简介Webgame设计与开发之内容简介内容简介      本书将webgame设计方法,编程方法,设计过程完全的结合起来,详细阐明webgame设计与开发的各个方面。本书首先介绍webgame的市场趋势,以及开发wengame所需要的主要技术,然后分成三大部分:客户端设计,服务端设计,数值设计。最后以一个完整的webgame游戏展现在读者面前。     本书结构紧凑,内容由浅入深,是学习

    2022年6月6日
    32
  • 东软始业教育内容提纲(2020)(后附题目)

    东软始业教育内容提纲(2020)(后附题目)东软始业教育内容提纲(后附题目)——2012年8月修正版写在前面:至各位兄弟姐妹,希望大家都能顺利通过!有些题目略微有些变动,总体内容不变。后面的题目绝大多数都在列了,剩余的注意搜索关键字,在前面提纲部分一定有!在百度上搜索一次只能搜索三页,建议大家先下载下来,准备好再开始考试,这样可以全篇搜索。40分的极致不是我的追求,38分这个绝对可以有!1)

    2022年5月10日
    49
  • WINDOWS 1.0模拟器_modelsim激活成功教程版安装教程

    WINDOWS 1.0模拟器_modelsim激活成功教程版安装教程本教程包括软件下载、激活成功教程文件下载、安装激活成功教程方法,助你一次成功。 软件安装好了却不能用,想必大家都有过这样的痛苦和无奈。这款软件的激活成功教程花了我整整一个下午的时间,期间在网上找了各种方法尝试均以失

    2022年8月3日
    10
  • 下载pycharm安装教程_最新安卓开发工具使用方法

    下载pycharm安装教程_最新安卓开发工具使用方法Pycharm可以说是一款进行Python开发功能强大的编辑器。鉴于近期很少有关最新版本的Pycharm安装教程,为了能够帮助刚刚学习Python的朋友们。宋宋老师又给大家准备了一版,会介绍Pycharm的安装和注意事项,希望能够在大家学习Python的道路上起到一点微薄之力。文章分为四部分: Pycharm简介 Pycharm下载 Pycharm安装 Pycharm配置 Pycharm简介PyCharm是由JetBrains打造的一款PythonIDE,J

    2022年8月28日
    9
  • ubuntu分区方案(合并分区)

    MostPCoperatingsystemsstillworkwithanancientdiskpartitionschemethathistoricallymakesdistinctionbetweenprimaryandextendedpartitions.Italsoplacesalimitationforfourprimarypart

    2022年4月14日
    535
  • 苹果绿设置

    苹果绿设置设置方法如下 在桌面点右键选 属性 properties 接着点 外观 appearance 点右下角的 高级 advanced 然后在 项目 items 的下拉菜单里选 窗口 windows 再点它右侧的下拉菜单 颜色 color 点下方的 其它 others 然后把 色调 Hue 设为 85 饱和度 Sat 设为 90 亮度 Lum 设为 205 产品出厂

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号