从零开始学习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);
    }
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 基于pytorch卷积人脸表情识别–毕业设计「建议收藏」

    基于卷积神经网络的人脸表情识别前言毕业设计内容介绍卷积神经网络的设计卷积网络的模型卷积池化过程详细说明第一层卷积池化过程第二层卷积池化过程第三层卷积池化过程全连接层过程模型的训练过程卷积与池化原理模型如何训练模型的评估指标训练结果分析通过训练曲线分析通过混淆矩阵分析效果通过摄像头识别表情设计流程效果演示部分代码展示总结前言这篇文章记录一下我本科毕业设计的内容。我的课题是人脸表情识别,本来最开始按照历届学长的传统是采用MATLAB用传统的机器学习方法来实现分类的。但是鉴于我以前接触过一点点深度学习的内容,

  • 基于arduino的光控窗帘_基于Arduino系统的智能窗帘设计与实现.doc

    基于arduino的光控窗帘_基于Arduino系统的智能窗帘设计与实现.doc摘要:跟随社会发展的潮流,现代科学技术正处于快速发展阶段,人们对智能家居的关注度也越来越高,人们开始寻求更加智能和舒适的生活及办公环境。智能遥控属于电子与信息工程的一个重要分支,在现代智能家居中有着良好的发展前景。本设计采用Arduino单片机来控制智能窗帘系统,实时监测室内温湿度情况并在LCD上显示,使用了红外遥控的技术,可以切换不同的工作模式从而来切换其控制方式,实现半自动控制、自动控制以及远…

  • 获取activexobject对象失败_js获取对象

    获取activexobject对象失败_js获取对象js

    2022年10月14日
  • 汇编介绍

    汇编介绍汇编大多是指汇编语言,汇编程序。把汇编语言翻译成机器语言的过程称为汇编。在汇编语言中,用助记符(Memoni)代替操作码,用地址符号(Symbol)或标号(Label)代替地址码。这样用符号代替机器语言的二进制码,就把机器语言变成了汇编语言。于是汇编语言亦称为符号语言。用汇编语言编写的程序,机器不能直接识别,要由一种程序将汇编语言翻译成机器语言,这种起翻译作用的程序叫汇编程序…

    2022年10月13日
  • sql优化的几种方式

    sql优化的几种方式一、为什么要对SQL进行优化我们开发项目上线初期,由于业务数据量相对较少,一些SQL的执行效率对程序运行效率的影响不太明显,而开发和运维人员也无法判断SQL对程序的运行效率有多大,故很少针对SQL进行专门的优化,而随着时间的积累,业务数据量的增多,SQL的执行效率对程序的运行效率的影响逐渐增大,此时对SQL的优化就很有必要。二、SQL优化的一些方法1.对查询进行优化,应尽量避免全表扫描…

  • 微信小程序:微信登陆(ThinkPHP作后台)

    微信小程序:微信登陆(ThinkPHP作后台)

    2021年10月14日

发表回复

您的电子邮箱地址不会被公开。

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