【uTenux实验】内存池管理(固定内存池和可变内存池)

1、固定内存池管理实验

      内存管理是操作系统的一个基础功能。uTenux的内存池管理函数提供了基于软件的内存池管理和内存块分配管理。uTenux的内存池有固定大小的内存池和大小可变的内存池之分,它们被看成是互相独立的对象,需要不同的系统调用集来进行操作。

内存池管理函数管理的内存全部包含在系统空间以内。

1、固定尺寸内存池实验

    固定尺寸内存池是一个用来动态管理固定尺寸内存块的对象。每个固定尺寸内存池都有一个用作固定尺寸内存池的内存空间(简称为内存池区)和一个等待内存块分配的任务队列。

    uTenux提供了五个供操作固定内存池的API,分别用于创建、删除、获取、释放、查询内存池状态。

创建固定尺寸内存池,需要提供一个T_CMPF类型的结构体。定义如下:

typedef    struct t_cmpf {
    VP                  exinf;                   /* 扩展信息*/
    ATR                 mpfatr;                     /* 内存池属性*/
    W                   mpfcnt;                     /* 创建的内存池块数*/
    W                   blfsz;                     /* 每块的大小(byte) */
    UB                  dsname[8];               /* Object name */
    VP                  bufptr;                     /* User buffer */
} T_CMPF;

内存池属性mpfatr是的定义如下:

mpfatr:=(TA_TFIFO||TA_TPRI)|TA_USERBUF|TA_DSNAME|(TA_RNG0||TA_RNG1 ||TA_RNG2||TA_RNG3)

TA_TFIFO 等待内存分配的任务按FIFO的顺序排队

TA_TPRI 等待内存分配的任务按优先级顺序排队

TA_RNGn 内存访问权设置成保护级别n

TA_USERBUF 指示系统使用用户指定的内存空间

TA_DSNAME 设定DS对象名

      由于uTenux没有使用硬件的内存保护功能,TA_RNGn 只能是TA_RNG0。TA_USERBUF是不使用OS提供的内存空间时候使用的。TA_DSNAME基本没什么用。

获取内存池函数:ER ercd= tk_get_mpf(ID mpfid,VP *p_blf,TMO tmout);中,p_blf为获取的内存块的起始地址。为VP类型,即一个void*的指针。使用时需要注意。

【实验描述】

1、创建两个任务一个包含5块区域的内存池。

2、启动TaskB,在TaskB中启动TaskA,TaskA开始执行就立即休眠。等待TaskB唤醒

3、TaskB继续执行。先输出当前可用内存块数目,申请一块内存后再次输出内存块数目。

4、最后将一段数据放入这块内存。之后唤醒TaskA。

5、TaskA输出由TaskB放入内存块的数据之后,释放内存块。再次输出可用内存块数。。然后进入休眠状态,TaskB继续执行。

【代码及输出】

固定内存池使用:

#include "MpfSample.h"

void MpfSampleTaskA(W stacd,VP exinf);
void MpfSampleTaskB(W stacd,VP exinf);
void MpfSamplePutCnt(void);
static ID TaskID_A;
static ID TaskID_B;
static ID mpfid;
static VP blf=NULL;

ER MpfSample( void)
{
    ER ercd = E_OK;
    T_CTSK ctsk;
    T_CMPF cmpf;

    tm_putstring((UB*)"Mempool fix sample create Task A;
");
    ctsk.exinf = (VP)NULL;
    ctsk.tskatr = TA_HLNG | TA_RNG0;
    ctsk.task = MpfSampleTaskA;
    ctsk.itskpri = 24;
    ctsk.stksz = 512;
    TaskID_A = tk_cre_tsk(&ctsk);
    if(TaskID_A < E_OK) {
        ercd=TaskID_A;
        return ercd;
    }
    
    tm_putstring((UB*)"Mempool fix sample create Task B;
");
    ctsk.task = MpfSampleTaskB;
    ctsk.itskpri = 26;
    TaskID_B = tk_cre_tsk(&ctsk);
    if(TaskID_B < E_OK) {
        ercd=TaskID_B;
        return ercd;
    }
    
    tm_putstring((UB*)"Mempool fix sample create a mempoolf;
");
    cmpf.bufptr = NULL;
    cmpf.exinf = (VP)NULL;
    cmpf.blfsz = 100;   //100Byte
    cmpf.mpfcnt = 5;  //5块
    cmpf.mpfatr = TA_TFIFO | TA_RNG0;  //内存池属性,先到先得 不使用保护
    mpfid = tk_cre_mpf(&cmpf);
 
    tm_putstring((UB*)"Mempool fix sample create a mempoolf successfully;
");
    MpfSamplePutCnt();

    tm_putstring((UB*)"Mempool fix sample start Task B;
");
    tk_sta_tsk(TaskID_B,0);
    return TRUE;
} 
 
void MpfSampleTaskA(W stacd,VP exinf)
{
  tm_putstring((UB*)"Task A now enter sleep mode
");
  tk_slp_tsk(-1);
  while(1)
  {
    tm_putstring((UB*)"Task A read data from the memory block,that is:
");
    tm_putstring((UB*)blf);
    tk_rel_mpf(mpfid,blf);
    MpfSamplePutCnt();
    tk_slp_tsk(-1);
  }
}

void MpfSampleTaskB(W stacd,VP exinf)
{
  B* buf = "this is utenux
";
  B blfadd[10];
  tm_putstring((UB*)"Task B now start Task A
");
  tk_sta_tsk(TaskID_A,0);
  tm_putstring((UB*)"*****************************************
");
  while(1)
  {
    MpfSamplePutCnt();
    tm_putstring((UB*)"Task B now get a block of memory
");
    if(E_OK == tk_get_mpf(mpfid,&blf,-1))  //申请内存块
    {
      MpfSamplePutCnt();
      tm_putstring((UB*)"Task B get memory block sucessfully
memory addressis 0x");
      ltostr((UW)blf,blfadd,16,10);
      tm_putstring((UB*)blfadd);  //以字符串形式输出地址
      tm_putstring((UB*)"
*****************************************
");
      memcpy(blf,buf,strlen(buf)+1);
    }
    else
    {
      tm_putstring((UB*)"Task B failed get memory
");
      break;
    }
    tm_putstring((UB*)"Task B now wake up Task A
");
    tk_wup_tsk(TaskID_A);
    Delay(0x1000000);
  }
}

void MpfSamplePutCnt(void)
{
    B frbcnt[10];
    T_RMPF rmpf;

    tm_putstring((UB*)"Now Free memory block number is ");
    tk_ref_mpf(mpfid, &rmpf);
    ltostr(rmpf.frbcnt,frbcnt,10,10);
    tm_putstring((UB*)frbcnt);
    tm_putstring((UB*)"
");
}

输出:

----------------------------------------------------
        micro Tenux Version 1.6.00(build 0180)     
            Supported MCU is ST STM32F407VG        
  Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd. 
----------------------------------------------------

Mempool fix sample create Task A;
Mempool fix sample create Task B;
Mempool fix sample create a mempoolf;
Mempool fix sample create a mempoolf successfully;
Now Free memory block number is 5
Mempool fix sample start Task B;
Task B now start Task A
Task A now enter sleep mode
*****************************************
Now Free memory block number is 5
Task B now get a block of memory
Now Free memory block number is 4
Task B get memory block sucessfully
memory addressis 0x20002634
*****************************************
。。。。。。。。。。。

2、可变内存池实验

预备知识:C语言中的内存块操作函数

1、memcpy
原型:void *memcpy(void *dest, const void *src, size_t n);
功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
2、memmove

原型:void *memmove( void* dest, const void* src,size_tcount );

头文件:<string.h>

功能:由src所指内存区域复制count个字节到dest所指内存区域。

说明memmove用于从src拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

3、memcmp
int memcmp(const void *buf1, const void *buf2, unsigned int count);
比较内存区域buf1和buf2的前count个字节。

当buf1<buf2时,返回值<0

当buf1=buf2时,返回值=0

当buf1>buf2时,返回值>0

4、memchr

原型:extern void *memchr(const void *buf, int ch, size_t count);

用法:#include <string.h>

功能:从buf所指内存区域的前count个字节查找字符ch。

说明:当第一次遇到字符ch时停止查找。如果成功,返回指向字符ch的指针;否则返回NULL。

5、memset

void *memset(void *s, int ch, size_t n);

函数解释:将s中前n个字节(typedef unsigned int size_t)用 ch 替换并返回s。

作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法

可变尺寸内存池是一个用来动态管理任何大小的内存块的对象。与固定尺寸内存池一样,每个可变尺寸内存池都有一个用作可变尺寸内存池的内存空间和一个等待内存块分配的任务队列。

可变尺寸内存池管理SVC:

创建可变尺寸内存池:tk_cre_mpl

需要提供参数T_CMPL的定义如下:

typedef    struct t_cmpl {
    VP                  exinf;                     /* 扩展属性*/
    ATR                 mplatr;                     /* 内存池属性 */
    W                   mplsz;                     /* 内存池的大小 (byte) */
    UB                  dsname[8];               /* Object name */
    VP                  bufptr;                     /* User buffer */
} T_CMPL;

内存池属性的定义:

mplatr:=(TA_TFIFO||TA_TPRI)|TA_USERBUF|TA_DSNAME|(TA_RNG0||TA_RNG1||TA_RNG2 ||TA_RNG3)

TA_TFIFO 等待内存分配的任务按FIFO的顺序排队

TA_TPRI 等待内存分配的任务按优先级顺序排队

TA_RNGn 内存访问权设置成保护级别n

TA_USERBUF 指示系统使用用户指定的内存空间

TA_DSNAME 设定DS对象名

可以发现,uTenux中各个对象的属性定义是很相似的:

1、两种排队顺序TA_TFIFO和TA_TPRI

2、内存保护级别TA_RNGn 必须是TA_RNG0

3、TA_DSNAME对象名

4、TA_USERBUF 用户缓存

之后就不列出来这东西了。

至于申请,释放都比较简单,这里不一一列出了。

【实验描述】

本实验参考源码包中的09.mempoolv

1、首先创建两个人物一个大小512字节的内存池。然后启动任务A,任务A自动休眠等待唤醒。之后启动任务B。

2、任务B首先输出可用内存数,之后申请128Byte内存,再次输出可用内存数。

3、任务B将申请到的内存清空,将数据放入。之后唤醒任务A。

4、任务A优先级高,开始执行。

5、任务A首先输出任务B放入内存块的数据,任何释放这个内存块。

6、最后任务A再次休眠,任务B开始循环执行。

【实验代码和输出】

#include "MempoolvSample.h"

void MplSampleTaskA(W stacd,VP exinf);
void MplSampleTaskB(W stacd,VP exinf);
void MplSamplePutCnt(void);
static ID TaskID_A;
static ID TaskID_B;
static ID mplid;
static VP blf;

ER MplSample( void)
{
    ER ercd = E_OK;
    T_CTSK ctsk;
    T_CMPL cmpl;

    tm_putstring((UB*)"Mempool variable sample create Task A;
");
    ctsk.exinf = (VP)NULL;
    ctsk.tskatr = TA_HLNG | TA_RNG0;
    ctsk.task = MplSampleTaskA;
    ctsk.itskpri = 24;
    ctsk.stksz = 512;
    TaskID_A = tk_cre_tsk(&ctsk);
    if(TaskID_A < E_OK) {
        ercd=TaskID_A;
        PutErcd(ercd);
        return ercd;
    }
    
    tm_putstring((UB*)"Mempool variable sample create Task B;
");
    ctsk.exinf = (VP)NULL;
    ctsk.tskatr = TA_HLNG | TA_RNG0;
    ctsk.task = MplSampleTaskB;
    ctsk.itskpri = 26;
    ctsk.stksz = 512;
    TaskID_B = tk_cre_tsk(&ctsk);
    if(TaskID_B < E_OK) {
        ercd=TaskID_B;
        PutErcd(ercd);
        return ercd;
    }
    
    //自己写
    tm_putstring((UB*)"Mempool variable sample create a mempool variable;
");
   
    cmpl.bufptr = NULL;
    cmpl.exinf =(VP)NULL;
    cmpl.mplatr = TA_TFIFO | TA_RNG0;
    cmpl.mplsz = 512;
    mplid = tk_cre_mpl(&cmpl);
    if(E_OK < mplid)
    {
      tm_putstring((UB*)"Mempool variable sample create a mempool successfully;
");
    }
      
    tm_putstring((UB*)"Mempool variable sample start Task A;
");
    tk_sta_tsk(TaskID_A,0);
    tm_putstring((UB*)"Mempool variable sample start Task B;
");
    tk_sta_tsk(TaskID_B,0);

    return TRUE;
} 
 
void MplSampleTaskA(W stacd,VP exinf)
{
  tm_putstring((UB*)"Task A enter sleep status;
");
  tk_slp_tsk(-1);
  while(1)
  {
    tm_putstring((UB*)"Task A print the contents of the memory block;
");
    tm_putstring((UB*)blf);
    tm_putstring((UB*)"Task A release mpl,;
");
    tk_rel_mpl(mplid,blf);
    MplSamplePutCnt();
    
    tk_slp_tsk(-1);
  }
}

void MplSampleTaskB(W stacd,VP exinf)
{
  B* buf = "this is an utenux programme
";
  B* blfadd[10];//用于存放地址转换来的字符串
  
  tm_putstring((UB*)"this is in task b
");
  while(1)
  {
    MplSamplePutCnt(); //输出申请mpl之前的可用内存字节数
    if(E_OK == tk_get_mpl(mplid,128,&blf,-1))
    {
      MplSamplePutCnt();
      memset(blf,0,128);//清除掉获取的内存中所有数据
      memcpy(blf,buf,strlen(buf));
      
    }
    Delay(0x1000000);
    tm_putstring((UB*)"Task B wake up Task A
");
    tk_wup_tsk(TaskID_A);
  }
}

void MplSamplePutCnt(void)
{
    B frsz[10];
    T_RMPL rmpl;

    tm_putstring((UB*)"Now Free memory number is ");
    tk_ref_mpl(mplid, &rmpl);
    ltostr(rmpl.frsz,frsz,10,10);
    tm_putstring((UB*)frsz);
    tm_putstring((UB*)"B
");
}

输出:
----------------------------------------------------
        micro Tenux Version 1.6.00(build 0180)     
            Supported MCU is ST STM32F407VG        
  Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd. 
----------------------------------------------------

Mempool variable sample create Task A;
Mempool variable sample create Task B;
Mempool variable sample create a mempool variable;
Mempool variable sample create a mempool successfully;
Mempool variable sample start Task A;
Task A enter sleep status;
Mempool variable sample start Task B;
this is in task b
Now Free memory number is 512B
Now Free memory number is 376B
Task B wake up Task A
Task A print the contents of the memory block;
this is an utenux programme
Task A release mpl,;
Now Free memory number is 512B
。。。。。。

【关于可变内存池使用的内存与生育内存相加不等于总内存数的问题】

我在实验中每次申请128B,剩余可用内存为376B,每次申请都要损失8B内存,释放后还是原来的数目。后来看到这段,算是明白了:

Get memory block,'blksz' must be larger than minimum fragment size and adjusted by ROUNDSZ unit.

#define ROUNDSZ(sz)    (((UW)(sz) + (UW)(ROUNDSIZE-1)) & ~(UW)(ROUNDSIZE-1))

应该是一个内存对齐。申请121~128个block都会剩余376B

每个可变内存池都是一个队列,队列中每块内存大小都是ROUNDSZ。申请内存的时候,只能按照ROUNDSZ大小进行分配少补多不退。。。

原文地址:https://www.cnblogs.com/zyqgold/p/3169195.html