系统相关
首页 > 系统相关> > μC/OS-II--内存管理

μC/OS-II--内存管理

作者:互联网

在μC/OS-II中,将大块内存作为一个分区,在系统中有多个分区,每个分区又分为整数个大小相同的内存块,由于大小相同,分配和释放时间相同,应用程序根据需要从不同的分区得到不同的内存块,内存块释放时,放回以前的位置。

内存控制块

为了跟踪每个内存分区,引入内存控制块进行管理。OSMemAddr为指向内存分区起始地址的指针,当调用OSMemCreate时初始化,之后不能修改。OSMemFreeList为指向下一个空余内存控制块或下一个空余内存块的指针。OSMemBlkSize在建立该内存分区时建立,表示内存分区中单个内存块的大小。OSMemNBlks在建立该内存分区时建立,表示内存分区中总的内存块数量。OSMemNFree为内存分区中当前空余内存块数量。

typedef struct os_mem { 
void   *OSMemAddr;                  //指向内存分区起始地址的指针
void   *OSMemFreeList;           //   内存指向下一个空余内存块的指针 
INT32U  OSMemBlkSize;         //内存分区中内存块的大小
INT32U  OSMemNBlks;           //  内存分区中总的内存块数量   
INT32U  OSMemNFree;         //    内存分区中当前空余内存块数量  
#if OS_MEM_NAME_SIZE > 1
    INT8U   OSMemName[OS_MEM_NAME_SIZE];  //分区名
#endif
} OS_MEM;//分区控制块结构体

内存块管理初始化

#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_EXT  OS_MEM           *OSMemFreeList;           //指向空闲内存分区的指针
OS_EXT  OS_MEM            OSMemTbl[OS_MAX_MEM_PART];//内存块表
#endif
void  OS_MemInit (void)
{
#if OS_MAX_MEM_PART == 1//内存分区大小为1
    OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl));  //清空所有内存控制块区域          
    OSMemFreeList               = (OS_MEM *)&OSMemTbl[0];//将OSMemFreeList指向OSMemTbl第一个内存控制块 
#if OS_MEM_NAME_SIZE > 1
    OSMemFreeList->OSMemName[0] = '?';           //内存分区名初始化为未知名      
    OSMemFreeList->OSMemName[1] = OS_ASCII_NUL;
#endif
#endif
#if OS_MAX_MEM_PART >= 2//内存分区大小大于2
    OS_MEM  *pmem;
    INT16U   i;


    OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl));  //清空所有内存控制块区域         
    pmem = &OSMemTbl[0];                                 //将OSMemFreeList指向OSMemTbl第一个内存控制块 
    for (i = 0; i < (OS_MAX_MEM_PART - 1); i++) {        
        pmem->OSMemFreeList = (void *)&OSMemTbl[i+1];    //将所有内存控制块通过OSMemFreeList指针链接
#if OS_MEM_NAME_SIZE > 1
        pmem->OSMemName[0]  = '?';                       //内存分区名初始化为未知名     
        pmem->OSMemName[1]  = OS_ASCII_NUL;
#endif
        pmem++;//指向下一个内存控制块
    }
    pmem->OSMemFreeList = (void *)0;                     //最后一个内存控制块的OSMemFreeList为空
#if OS_MEM_NAME_SIZE > 1
    pmem->OSMemName[0]  = '?';                           //内存分区名初始化为未知名
    pmem->OSMemName[1]  = OS_ASCII_NUL;
#endif

    OSMemFreeList       = &OSMemTbl[0];                  //将OSMemFreeList指向OSMemTbl第一个内存控制块 
#endif
}

首先判断分区数量是否为1,如果是,清空内存控制块,将OSMemFreeList指向OSMemTbl第一个内存控制块,然后分区名是否大于1,大于1的话,初始化分区名为未知名。若分区数量大于等于2,如果是,清空内存控制块,将pmem指向OSMemTbl第一个内存控制块,将所有内存控制块通过OSMemFreeList指针链接,所有分区初始化分区名为未知名,最后一个内存控制块的OSMemFreeList指向空,OSMemFreeList指向OSMemTbl第一个内存控制块。

创建内存分区

在使用一个内存分区之前,必须建立该内存分区,使用OSMemCreate创建内存分区,函数共四个参数,内存分区的起始地址addr,分区内内存块总数nblks,每个内存块的字节数blksize,指向出错信息的指针perr。

OS_MEM  *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *perr)
{
    OS_MEM    *pmem;
    INT8U     *pblk;
    void     **plink;
    INT32U     i;
#if OS_CRITICAL_METHOD == 3                          //为CPU状态寄存器分配内存
    OS_CPU_SR  cpu_sr = 0;
#endif



#if OS_ARG_CHK_EN > 0              
    if (perr == (INT8U *)0) {                         //传进来的perr为空,不正确,返回空内存控制块地址
        return ((OS_MEM *)0);
    }
    if (addr == (void *)0) {                          //传进来的addr为空,不正确,返回空内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_ADDR,表示无效地址
        *perr = OS_ERR_MEM_INVALID_ADDR;
        return ((OS_MEM *)0);
    }
    if (((INT32U)addr & (sizeof(void *) - 1)) != 0){ //地址对齐,为4的倍数
        *perr = OS_ERR_MEM_INVALID_ADDR;
        return ((OS_MEM *)0);
    }
    if (nblks < 2) {                                  //分区数至少两个,返回空内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_BLKS,表示无效地址
        *perr = OS_ERR_MEM_INVALID_BLKS;
        return ((OS_MEM *)0);
    }
    if (blksize < sizeof(void *)) {                   //必须使单个内存块大小至少能存一个指针,因为同一个分区内存块通过链表串联
        *perr = OS_ERR_MEM_INVALID_SIZE;
        return ((OS_MEM *)0);
    }
#endif
    OS_ENTER_CRITICAL();
    pmem = OSMemFreeList;                             //pmem指向空闲内存控制块
    if (OSMemFreeList != (OS_MEM *)0) {              //指向下一个空闲内存控制块
        OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
    }
    OS_EXIT_CRITICAL();
    if (pmem == (OS_MEM *)0) {                        //确定是否有一个内存分区
        *perr = OS_ERR_MEM_INVALID_PART;
        return ((OS_MEM *)0);
    }
    plink = (void **)addr;                           //plink为指向指针的指针,将addr进行强制转换
    pblk  = (INT8U *)((INT32U)addr + blksize);       //获取第二个区块的起始地址
    for (i = 0; i < (nblks - 1); i++) {
       *plink = (void *)pblk;                         //指向下一个内存块地址
        plink = (void **)pblk;                        //指向指针的指针后移一个内存块
        pblk  = (INT8U *)((INT32U)pblk + blksize);    //获得下一个区块起始地址
    }
    *plink              = (void *)0;                  //最后一个内存块指向为空
    pmem->OSMemAddr     = addr;                       //填充起始地址
    pmem->OSMemFreeList = addr;                       //填充空闲地址
    pmem->OSMemNFree    = nblks;                      //填充可用内存块数
    pmem->OSMemNBlks    = nblks;                      //填充总内存块数
    pmem->OSMemBlkSize  = blksize;                    //填充每个内存块的大小
    *perr               = OS_ERR_NONE;                //返回无错误
    return (pmem);
}

首先为CPU状态寄存器分配内存,检查perr是否为空,空的话返回空内存控制块地址,检查addr是否正确,不正确返回空的内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_ADDR,即无效地址。检查分区数是否大于两个,如果不是,返回空地址指针,并且返回错误类型OS_ERR_MEM_INVALID_BLKS,即无效的块数量,必须使单个内存块大小至少能存一个指针,因为同一个分区内存块通过链表串联,否则返回空地址指针,并且返回错误类型OS_ERR_MEM_INVALID_SIZE,即无效的块大小。检查完成后,通过OSMemFreeList获取一个空闲块,并将OSMemFreeList指向下一个空闲快,然后检查获得的空闲块是否存在,如果不存在,返回空的内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_PART,即无效分区。然后将内存空间中的所有内存块连接成单向链表。plink为指向指针的指针,pblk为指针,指向各个内存块首地址,首先将plink指向第一个内存块的指针域,pblk赋值为第二个内存块指针域的值,最后循环做若下操作,将plink指向的指针域指向下一个内存块指针域,plink指向下一个内存块指针域,然后将下一个内存块指针域的值赋值给pblk,最后一个内存块指向为空。最后填充起始地址,空闲地址,可用内存块数,总内存块数,每个内存块的大小,返回无错误的perr状态。

OSMemCreate函数创建分区完成后,内存控制块与对应的内存分区,分区内的内存块之间的关系如下:

分配一个内存块

调用OSMemGet函数,从已经建立的内存分区中申请一个内存块,传入参数为pmem,指向特定分区内存控制块的指针,指向出错信息的指针perr,返回可用内存块地址。

void  *OSMemGet (OS_MEM *pmem, INT8U *perr)
{
    void      *pblk;
#if OS_CRITICAL_METHOD == 3                         //为CPU状态寄存器分配内存
    OS_CPU_SR  cpu_sr = 0;
#endif



#if OS_ARG_CHK_EN > 0
    if (perr == (INT8U *)0) {                          //传进来的perr为空,不正确,返回空地址
        return ((void *)0);
    }
    if (pmem == (OS_MEM *)0) {                        //传进来对应的分区内存控制块为不存在,返回空地址,返回错误信息OS_ERR_MEM_INVALID_PMEM
        *perr = OS_ERR_MEM_INVALID_PMEM;
        return ((void *)0);
    }
#endif
    OS_ENTER_CRITICAL();
    if (pmem->OSMemNFree > 0) {                       //检查该分区中是否有可用的内存块
        pblk                = pmem->OSMemFreeList;    //如果是,pllk指向下一个可用内存块
        pmem->OSMemFreeList = *(void **)pblk;         //调整下一个可用内存块地址
        pmem->OSMemNFree--;                           //可用内存块数量减一
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;                          //无错误
        return (pblk);                               //返回可用内存苦熬地址
    }
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_MEM_NO_FREE_BLKS;                 // 返回无空余内存块
    return ((void *)0);                              //返回空指针          
}

首先为CPU状态寄存器分配内存,检查传进来的perr是否为空,是的话返回空地址,判断传进来对应的分区内存控制块是否不存在,不存在返回空地址,返回错误信息OS_ERR_MEM_INVALID_PMEM,即无效的分区内存控制块指针。然后检查该分区中是否有可用的内存块,没有的话返回OS_ERR_MEM_NO_FREE_BLKS,即无空余内存块,返回空指针。如果有可用内存块,如果是,pllk指向可用内存块,调整下一个可用内存块地址,可用内存块数量减一,perr返回无错误,返回可用内存块地址。

释放一个内存块

当不在使用内存块时,使用函数OSMemPut释放这个内存块,放回到相应的内存分区中。传入参数为特定分区内存控制块指针pmem,内存块指针pblk。

INT8U  OSMemPut (OS_MEM *pmem, void *pblk)
{
#if OS_CRITICAL_METHOD == 3                      //为CPU状态寄存器分配内存
    OS_CPU_SR  cpu_sr = 0;
#endif



#if OS_ARG_CHK_EN > 0
    if (pmem == (OS_MEM *)0) {                   //检查是否是一个有效的内存控制块指针
        return (OS_ERR_MEM_INVALID_PMEM);
    }
    if (pblk == (void *)0) {                    //检查是否是一个有效的内存块地址
        return (OS_ERR_MEM_INVALID_PBLK);
    }
#endif
    OS_ENTER_CRITICAL();
    if (pmem->OSMemNFree >= pmem->OSMemNBlks) {  //内存块全在使用中
        OS_EXIT_CRITICAL();
        return (OS_ERR_MEM_FULL);
    }
    *(void **)pblk      = pmem->OSMemFreeList;   //将归还的内存空间插入空余内存块链表的表头
    pmem->OSMemFreeList = pblk;                 //
    pmem->OSMemNFree++;                         //分区中剩余内存块数量加1
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);                        
}

首次为CPU状态寄存器分配内存,检查是否是一个有效的内存控制块指针,检查是否是一个有效的内存块地址。检查内存块全未使用,是的话返回OS_ERR_MEM_FULL,空闲内存块已满。将归还的内存空间插入空余内存块链表的表头,分区中剩余内存块数量加1,返回无错误。

查询内存分区状态

可以使用OSMemQuery函数查询特定分区的有关信息,通过该函数能知道内存分区内存块大小,可用内存块数目,已经使用了的内存块数量。

typedef struct os_mem_data {
    void   *OSAddr;            //指向内存分区首地址的指针
    void   *OSFreeList;        //指向空闲内存块链表首地址的指针
    INT32U  OSBlkSize;         //每个内存块所含的字节数
    INT32U  OSNBlks;          //内存分区总的内存块数
    INT32U  OSNFree;          //空闲内存块总数
    INT32U  OSNUsed;          //正在使用的内存块总数
} OS_MEM_DATA;
#if OS_MEM_QUERY_EN > 0
INT8U  OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data)
{
#if OS_CRITICAL_METHOD == 3                      //为CPU状态寄存器分配内存
    OS_CPU_SR  cpu_sr = 0;
#endif



#if OS_ARG_CHK_EN > 0
    if (pmem == (OS_MEM *)0) {                   //检查是否是一个有效的内存控制块指针
        return (OS_ERR_MEM_INVALID_PMEM);
    }
    if (p_mem_data == (OS_MEM_DATA *)0) {        //检查是否是一个有效的内存信息单元
        return (OS_ERR_MEM_INVALID_PDATA);
    }
#endif
    OS_ENTER_CRITICAL();
    p_mem_data->OSAddr     = pmem->OSMemAddr;   //内存分区首地址
    p_mem_data->OSFreeList = pmem->OSMemFreeList;//空闲内存块链表首地址
    p_mem_data->OSBlkSize  = pmem->OSMemBlkSize;//每个内存块所含的字节数
    p_mem_data->OSNBlks    = pmem->OSMemNBlks;//内存分区总的内存块数
    p_mem_data->OSNFree    = pmem->OSMemNFree;//空闲内存块总数
    OS_EXIT_CRITICAL();
    p_mem_data->OSNUsed    = p_mem_data->OSNBlks - p_mem_data->OSNFree;//正在使用的内存块总数
    return (OS_ERR_NONE);
}
#endif

首先,为CPU状态寄存器分配内存,检查是否是一个有效的内存控制块指针,是否是一个有效的内存信息单元,填充内存分区首地址,空闲内存块链表首地址,每个内存块所含的字节数,内存分区总的内存块数,空闲内存块总数,正在使用的内存块总数。

另外还有获取和设置内存分区名的函数

INT8U  OSMemNameGet (OS_MEM *pmem, INT8U *pname, INT8U *perr)

void  OSMemNameSet (OS_MEM *pmem, INT8U *pname, INT8U *perr)

标签:OSMemFreeList,MEM,分区,II,内存,OS,pmem
来源: https://blog.csdn.net/qq_15391889/article/details/90302001