内存管理

  一个处理器,在不断地分配和释放内存的过程中,一整块连续的内存被分散为很多离散的小块内存,这些叫做内存碎片,内存碎片过多会导致内存的浪费。uC/OS 的内存管理机制就是为了尽量减少内存碎片。大致的思路是一次性取出一个较大的内存分区,把这个内存分区分成若干个内存块,然后将内存块逐个串成单向链表。每次要用到内存块就从内存分区中取出一块,用完就放回去。这跟消息队列的消息池的使用原理是一样的。

  如果想要使用内存管理机制,就必须事先使能内存管理。内存管理的使能位于“os_cfg.h”。

                                             /* -------------------------- MEMORY MANAGEMENT ------------------------ */
#define OS_CFG_MEM_EN                   1u   //使能/禁用内存管理

OSMemCreate ()

  要使用 uC/OS 的内存管理必须先声明和创建内存管理对象,调用 OSMemCreate () 函数可以创建一个内存管理对象。注意,内存分区一经创建便不能删除,系统没有提供相应的删除函数。OSMemCreate () 函数的信息如下表所示。

  OSMemCreate () 函数的定义位于“os_mem.c:

void  OSMemCreate (OS_MEM       *p_mem,    //内存分区控制块
                   CPU_CHAR     *p_name,   //命名内存分区
                   void         *p_addr,   //内存分区首地址
                   OS_MEM_QTY    n_blks,   //内存块数目
                   OS_MEM_SIZE   blk_size, //内存块大小(单位:字节)
                   OS_ERR       *p_err)    //返回错误类型
{
#if OS_CFG_ARG_CHK_EN > 0u      
    CPU_DATA       align_msk;
#endif
    OS_MEM_QTY     i;
    OS_MEM_QTY     loops;
    CPU_INT08U    *p_blk;
    void         **p_link;               //二级指针,存放指针的指针
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                    // SR(临界段关中断只需保存SR),开中断时将该值还原。

#ifdef OS_SAFETY_CRITICAL                //如果使能了安全检测
    if (p_err == (OS_ERR *)0) {          //如果错误类型实参为空
        OS_SAFETY_CRITICAL_EXCEPTION();  //执行安全检测异常函数
        return;                          //返回,停止执行
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508               //如果使能了安全关键
    if (OSSafetyCriticalStartFlag == DEF_TRUE) { //如果在调用OSSafetyCriticalStart()后创建
       *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;  //错误类型为“非法创建内核对象”
        return;                                  //返回,停止执行
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u         //如果使能了中断中非法调用检测
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用
       *p_err = OS_ERR_MEM_CREATE_ISR;         //错误类型为“在中断中创建对象”
        return;                                //返回,停止执行
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                             //如果使能了参数检测
    if (p_addr == (void *)0) {                         //如果 p_addr 为空      
       *p_err   = OS_ERR_MEM_INVALID_P_ADDR;           //错误类型为“分区地址非法”
        return;                                        //返回,停止执行
    }
    if (n_blks < (OS_MEM_QTY)2) {                      //如果分区的内存块数目少于2
       *p_err = OS_ERR_MEM_INVALID_BLKS;               //错误类型为“内存块数目非法”
        return;                                        //返回,停止执行
    }
    if (blk_size < sizeof(void *)) {                   //如果内存块空间小于指针的
       *p_err = OS_ERR_MEM_INVALID_SIZE;               //错误类型为“内存空间非法”
        return;                                        //返回,停止执行
    }
    align_msk = sizeof(void *) - 1u;                   //开始检查内存地址是否对齐
    if (align_msk > 0u) {
        if (((CPU_ADDR)p_addr & align_msk) != 0u){     //如果分区首地址没对齐
           *p_err = OS_ERR_MEM_INVALID_P_ADDR;         //错误类型为“分区地址非法”
            return;                                    //返回,停止执行
        }
        if ((blk_size & align_msk) != 0u) {            //如果内存块地址没对齐     
           *p_err = OS_ERR_MEM_INVALID_SIZE;           //错误类型为“内存块大小非法”
            return;                                    //返回,停止执行
        }
    }
#endif
    /* 将空闲内存块串联成一个单向链表 */
    p_link = (void **)p_addr;                          //内存分区首地址转为二级指针
    p_blk  = (CPU_INT08U *)p_addr;                     //首个内存块地址
    loops  = n_blks - 1u;
    for (i = 0u; i < loops; i++) {                     //将内存块逐个串成单向链表
        p_blk +=  blk_size;                            //下一内存块地址
       *p_link = (void  *)p_blk;                       //在当前内存块保存下一个内存块地址
        p_link = (void **)(void *)p_blk;               //下一个内存块的地址转为二级指针 
    }
   *p_link             = (void *)0;                    //最后一个内存块指向空

    OS_CRITICAL_ENTER();                               //进入临界段
    p_mem->Type        = OS_OBJ_TYPE_MEM;              //设置对象的类型   
    p_mem->NamePtr     = p_name;                       //保存内存分区的命名     
    p_mem->AddrPtr     = p_addr;                       //存储内存分区的首地址     
    p_mem->FreeListPtr = p_addr;                       //初始化空闲内存块池的首地址 
    p_mem->NbrFree     = n_blks;                       //存储空闲内存块的数目   
    p_mem->NbrMax      = n_blks;                       //存储内存块的总数目
    p_mem->BlkSize     = blk_size;                     //存储内存块的空间大小  

#if OS_CFG_DBG_EN > 0u            //如果使能了调试代码和变量 
    OS_MemDbgListAdd(p_mem);      //将内存管理对象插入内存管理双向调试列表
#endif

    OSMemQty++;                   //内存管理对象数目加1

    OS_CRITICAL_EXIT_NO_SCHED();  //退出临界段(无调度)
   *p_err = OS_ERR_NONE;          //错误类型为“无错误”
}
OSMemCreate()

  如果使能了 OS_CFG_DBG_EN(位于“os_cfg.h”),创建内存管理对象时还会调用OS_MemDbgListAdd () 函数将该内存管理对象插入到一个内存管理调试列表,是为方便调试所设。

  OS_MemDbgListAdd () 函数的定义位于“os_mem.c”:

#if OS_CFG_DBG_EN > 0u                                  //如果使能了调试代码和变量 
void  OS_MemDbgListAdd (OS_MEM  *p_mem)                 //将内存管理对象插入到内存管理调试列表的最前端
{
    p_mem->DbgPrevPtr               = (OS_MEM *)0;      //将该对象作为列表的最前端
    if (OSMemDbgListPtr == (OS_MEM *)0) {               //如果列表为空
        p_mem->DbgNextPtr           = (OS_MEM *)0;      //该队列的下一个元素也为空
    } else {                                            //如果列表非空
        p_mem->DbgNextPtr           =  OSMemDbgListPtr; //列表原来的首元素作为该队列的下一个元素
        OSMemDbgListPtr->DbgPrevPtr =  p_mem;           //原来的首元素的前面变为了该队列
    }
    OSMemDbgListPtr                 =  p_mem;           //该对象成为列表的新首元素
}
#endif
OS_MemDbgListAdd()

OSMemGet ()

  OSMemGet () 函数用于向内存管理对象获取一个空闲内存块。OSMemGet () 函数的信息如下表所示

  OSMemGet () 函数的定义也位于“os_mem.c

void  *OSMemGet (OS_MEM  *p_mem, //内存管理对象
                 OS_ERR  *p_err) //返回错误类型
{
    void    *p_blk;
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                    // SR(临界段关中断只需保存SR),开中断时将该值还原。

#ifdef OS_SAFETY_CRITICAL                //如果使能了安全检测
    if (p_err == (OS_ERR *)0) {          //如果错误类型实参为空
        OS_SAFETY_CRITICAL_EXCEPTION();  //执行安全检测异常函数
        return ((void *)0);              //返回0(有错误),停止执行
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                 //如果使能了参数检测
    if (p_mem == (OS_MEM *)0) {            //如果 p_mem 为空            
       *p_err  = OS_ERR_MEM_INVALID_P_MEM; //错误类型为“内存分区非法”
        return ((void *)0);                //返回0(有错误),停止执行
    }
#endif

    CPU_CRITICAL_ENTER();                    //关中断
    if (p_mem->NbrFree == (OS_MEM_QTY)0) {   //如果没有空闲的内存块
        CPU_CRITICAL_EXIT();                 //开中断
       *p_err = OS_ERR_MEM_NO_FREE_BLKS;     //错误类型为“没有空闲内存块”  
        return ((void *)0);                  //返回0(有错误),停止执行
    }
    p_blk              = p_mem->FreeListPtr; //如果还有空闲内存块,就获取它
    p_mem->FreeListPtr = *(void **)p_blk;    //调整空闲内存块指针
    p_mem->NbrFree--;                        //空闲内存块数目减1
    CPU_CRITICAL_EXIT();                     //开中断
   *p_err = OS_ERR_NONE;                     //错误类型为“无错误”
    return (p_blk);                          //返回获取到的内存块
}
OSMemGet

OSMemPut ()

  OSMemPut () 函数用于把内存块退还回内存管理对象(内存分区)

  OSMemPut () 函数的定义也位于“os_mem.c”:

void  OSMemPut (OS_MEM  *p_mem,   //内存管理对象
                void    *p_blk,   //要退回的内存块
                OS_ERR  *p_err)   //返回错误类型
{
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                    // SR(临界段关中断只需保存SR),开中断时将该值还原。

#ifdef OS_SAFETY_CRITICAL                //如果使能了安全检测
    if (p_err == (OS_ERR *)0) {          //如果错误类型实参为空
        OS_SAFETY_CRITICAL_EXCEPTION();  //执行安全检测异常函数
        return;                          //返回,停止执行
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                  //如果使能了参数检测
    if (p_mem == (OS_MEM *)0) {             //如果 p_mem 为空                
       *p_err  = OS_ERR_MEM_INVALID_P_MEM;  //错误类型为“内存分区非法”
        return;                             //返回,停止执行
    }
    if (p_blk == (void *)0) {               //如果内存块为空
       *p_err  = OS_ERR_MEM_INVALID_P_BLK;  //错误类型为"内存块非法"
        return;                             //返回,停止执行
    }
#endif

    CPU_CRITICAL_ENTER();                    //关中断
    if (p_mem->NbrFree >= p_mem->NbrMax) {   //如果所有的内存块已经退出分区                
        CPU_CRITICAL_EXIT();                 //开中断
       *p_err = OS_ERR_MEM_FULL;             //错误类型为“内存分区已满”
        return;                              //返回,停止执行
    }
    *(void **)p_blk    = p_mem->FreeListPtr; //把内存块插入空闲内存块链表
    p_mem->FreeListPtr = p_blk;              //内存块退回到链表的最前端
    p_mem->NbrFree++;                        //空闲内存块数目加1
    CPU_CRITICAL_EXIT();                     //开中断
   *p_err              = OS_ERR_NONE;        //错误类型为“无错误”
}
OSMemPut

 

转载于:https://www.cnblogs.com/tianxxl/p/10390700.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值