zoukankan      html  css  js  c++  java
  • 内存管理

      一个处理器,在不断地分配和释放内存的过程中,一整块连续的内存被分散为很多离散的小块内存,这些叫做内存碎片,内存碎片过多会导致内存的浪费。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
  • 相关阅读:
    js中Unicode转义序列
    css相对定位和绝对定位
    C#默认以管理员身份运行程序
    asp.net判断是否代理
    JS上传图片选择后立即预览
    asp.net判断是刷新还是提交
    查询QQ好友的IP地址(二)
    查询QQ好友的IP地址(一)
    Hadoop综合大作业+补交平时作业
    熟悉常用的Hbase操作
  • 原文地址:https://www.cnblogs.com/tianxxl/p/10390700.html
Copyright © 2011-2022 走看看