os_mem.c(全)

54 阅读7分钟

本篇介绍内存管理方面的内容,具体如下:

  • 简单介绍内存管理

  • 新建内存单元OS_MEM  *OSMemCreate (void *addr,INT32U nblks,INT32U  blksize,INT8U  *perr)\

  • 获得内存块void  *OSMemGet (OS_MEM  *pmem,INT8U *perr)

  • 得到内存分区名称INT8U  OSMemNameGet (OS_MEM   *pmem,INT8U   **pname,INT8U    *perr)\

  • 给内存分区设置名称

  • 释放内存块INT8U  OSMemPut (OS_MEM  *pmem,void  *pblk)\

  • 查询内存分区信息INT8U  OSMemQuery (OS_MEM *pmem,OS_MEM_DATA  *p_mem_data)\

  • 初始化内存分区管理void  OS_MemInit (void)

内存管理介绍:

    我们都知道可以用malloc()和free()两个函数动态地分配内存和释放内存。但是,在嵌入式实时操作系统中,多次这样做会把原来很大的一块连续内存区域,逐渐地分割成许多非常小而且彼此又不相邻的内存区域,即内存碎片。内存碎片的大量存在不利于我们后续再分配内存。

    所以,在μC/OS-II中,操作系统把连续的大块内存按分区来管理。每个分区中包含有整数个大小相同的内存块。利用这种机制,μC/OS-II 对malloc()和free()函数进行了改进,使得它们可以分配和释放固定大小的内存块。这样还有一个好处就是malloc()和free()函数的执行时间是固定的。

    在一个系统中可以有多个内存分区。这样,用户的应用程序就可以从不同的内存分区中得到不同大小的内存块。但是,特定的内存块在释放时必须重新放回它以前所属于的内存分区。显然,采用这样的内存管理算法,上面的内存碎片问题就得到了解决。\

    为了便于内存的管理,在μC/OS-II中使用内存控制块(memory control blocks)的数据结构来跟踪每一个内存分区,系统中的每个内存分区都有它自己的内存控制块。内存控制块的定义如下(uc/os-ii中):\

typedef struct os_mem {  /* MEMORY CONTROL BLOCK  内存控制块    */
    void   *OSMemAddr;  /* Pointer to beginning of memory partition 指向内存分区的开始的指针 */
    void   *OSMemFreeList; /* Pointer to list of free memory blocks指向空闲内存块的指针 */
    INT32U  OSMemBlkSize;   /* Size (in bytes) of each block of memory内存分区中每个块的大小,用户建立该内存分区时指定 */
    INT32U  OSMemNBlks;  /* Total number of blocks in this partition该内存分区块的总数量*/
    INT32U  OSMemNFree;   /* Number of memory blocks remaining in this partition该分区剩余空闲块的数量 */
#if OS_MEM_NAME_EN > 0u
    INT8U  *OSMemName;    /* Memory partition name内存分区的名字*/
#endif
} OS_MEM;
typedef struct os_mem_data {
    void   *OSAddr;/* Pointer to the beginning address of the memory partition指向内存分区起始地址的指针*/
    void   *OSFreeList;/* Pointer to the beginning of the free list of memory blocks指向空闲内存块列表开始的指针   */
    INT32U  OSBlkSize;/* Size (in bytes) of each memory block 每个内存块的大小  */
    INT32U  OSNBlks; /* Total number of blocks in the partition 该分区中块的总数量 */
    INT32U  OSNFree;  /* Number of memory blocks free空闲内存块的数量  */
    INT32U  OSNUsed; /* Number of memory blocks used 已使用的内存块的数量 */
} OS_MEM_DATA;

新建内存分区OS_MEM  *OSMemCreate (void   *addr, INT32U  nblks,INT32U  blksize,INT8U  *perr):

源代码如下:

#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
/*2018/2/23
*********************************************************************************************************
*                                        CREATE A MEMORY PARTITION
*					新建内存单元
* Description : Create a fixed-sized memory partition that will be managed by uC/OS-II.
*描述:建立一个大小可调节的内存单元,uc/os-ii管理该单元
* Arguments   : addr     is the starting address of the memory partition
*参数:	--addr:内存单元的起始地址。内存区可以使用静态数组或在初始化时使用malloc()函数建立
*               nblks    is the number of memory blocks to create from the partition.
						 blksize  is the size (in bytes) of each block in the memory partition.
*		--nblks:需要的内存块的数目。块的大小是内存分区每一块的大小(字节为单位)
*               perr     is a pointer to a variable containing an error message which will be set by
*                        this function to either:
*                        OS_ERR_NONE              if the memory partition has been created correctly.
*                        OS_ERR_MEM_INVALID_ADDR  if you are specifying an invalid address for the memory
*                                                 storage of the partition or, the block does not align
*                                                 on a pointer boundary
*                        OS_ERR_MEM_INVALID_PART  no free partitions available
*                        OS_ERR_MEM_INVALID_BLKS  user specified an invalid number of blocks (must be >= 2)
*                        OS_ERR_MEM_INVALID_SIZE  user specified an invalid block size
*                                                   - must be greater than the size of a pointer
*                                                   - must be able to hold an integral number of pointers
*		--perr:指向错误码的指针。可以设置为以下值:
				OS_ERR_NONE:内存分区成功建立。
				OS_ERR_MEM_INVALID_ADDR:指定了非法地址或者为空指针
				OS_ERR_MEM_INVALID_PART:没有空闲的分区可以使用。
				OS_ERR_MEM_INVALID_BLKS:使用者指定了无效的内存块(内存块数要>=2)
				OS_ERR_MEM_INVALID_SIZE:使用者指定了无效的块大小:
										-必须比指针大;
										-指针为整数值
* Returns    : != (OS_MEM *)0  is the partition was created
*              == (OS_MEM *)0  if the partition was not created because of invalid arguments or, no
*                              free partition is available.
	返回值:!= (OS_MEM *)0:内存分区成功创建。
			== (OS_MEM *)0:内存分区没有被创建因为参数无效或者没有可用的空闲分区。
*********************************************************************************************************
*/

OS_MEM  *OSMemCreate (void   *addr,
                      INT32U  nblks,
                      INT32U  blksize,
                      INT8U  *perr)
{
    OS_MEM    *pmem;/*指向内存控制块的指针*/
    INT8U     *pblk;/*每块内存块的起始地址*/
    void     **plink;/*链接起始地址*/
    INT32U     loops;
    INT32U     i;/*内存包含的内存区数量*/
	#if OS_CRITICAL_METHOD == 3u 
		OS_CPU_SR  cpu_sr = 0u;
	#endif

	#ifdef OS_SAFETY_CRITICAL
		if (perr == (INT8U *)0) {
			OS_SAFETY_CRITICAL_EXCEPTION();
		}
	#endif

	#ifdef OS_SAFETY_CRITICAL_IEC61508
		if (OSSafetyCriticalStartFlag == OS_TRUE) {
			OS_SAFETY_CRITICAL_EXCEPTION();
		}
	#endif

	#if OS_ARG_CHK_EN > 0u
		if (addr == (void *)0) {   /*当内存起始地址为0时*/    
			*perr = OS_ERR_MEM_INVALID_ADDR;/*错误显示为(非法地址,即地址为空指针,无效)*/
			return ((OS_MEM *)0);
		}
		if (((INT32U)addr & (sizeof(void *) - 1u)) != 0u){ 
			*perr = OS_ERR_MEM_INVALID_ADDR;
			return ((OS_MEM *)0);
		}
		if (nblks < 2u) { /*内存块至少为2*/                               
			*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; /*得到空闲的内存分区*/
    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;/*/链接起始地址=内存分区起始地址*/
    pblk  = (INT8U *)addr;
    loops  = nblks - 1u;/*循环次数*/
    for (i = 0u; i < loops; i++) 
	{
        pblk +=  blksize;     /*每块内存的起始地址=内存分区起始地址+每块内存块大小 */
       *plink = (void  *)pblk;
        plink = (void **)pblk;
    }
    *plink              = (void *)0;/*最后一个链接指针指为空 */
    pmem->OSMemAddr     = addr; /* 存储内存分区开始地址 */
    pmem->OSMemFreeList = addr; /*内存空闲列表指向内存分区起始地址*/
    pmem->OSMemNFree    = nblks;/* 分区中空闲内存块数量=需要的内存块数目 */
    pmem->OSMemNBlks    = nblks;/*总的内存块数量=需要的内存块数目*/
    pmem->OSMemBlkSize  = blksize;/*内存块大小 */
    *perr = OS_ERR_NONE;
    return (pmem);
}

其中一些语句解释如下:(个人理解)

1.

 pmem = OSMemFreeList; /*得到空闲的内存分区*/
    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);
    }

这里想说一下:我们看到判断了两次,第一次是判断有没有空闲的内存分区。这个是为了更新空闲列表设置的。第二次是判断有没有获得内存分区。有空闲分区我们不一定会获得,所以需要进行确认。

2.for循环中:

    for (i = 0u; i < loops; i++) 
	{
        pblk +=  blksize;     /*每块内存的起始地址=内存分区起始地址+每块内存块大小 */
       *plink = (void  *)pblk;
        plink = (void **)pblk;
    }

这里两行语句没有注释,因为觉得说不清楚。个人理解是:

\

1).pblk每次进行计算,保存的是下一个内存块起始地址。

2)plink指向pblk指向的那个地址;

3)plink本身作为下一个块的链接。

\

获得内存块void  *OSMemGet (OS_MEM  *pmem,INT8U   *perr):\

/*$PAGE*/
/*2018/2/23
*********************************************************************************************************
*                                          GET A MEMORY BLOCK
*				        获得内存块
* Description : Get a memory block from a partition
*描述:从内存分区得到内存块
* Arguments   : pmem    is a pointer to the memory partition control block
*参数:	--pmem:指向内存分区控制块的指针
*               perr    is a pointer to a variable containing an error message which will be set by this
*                       function to either:
*                       OS_ERR_NONE             if the memory partition has been created correctly.
*                       OS_ERR_MEM_NO_FREE_BLKS if there are no more free memory blocks to allocate to caller
*                       OS_ERR_MEM_INVALID_PMEM if you passed a NULL pointer for 'pmem'
*		--perr:包含错误码的指针:
			OS_ERR_NONE:内存分区被成功创建
			OS_ERR_MEM_NO_FREE_BLKS:没有空余的内存块可以分配
			OS_ERR_MEM_INVALID_PMEM:pmem为空指针。
* Returns     : A pointer to a memory block if no error is detected
*               A pointer to NULL if an error is detected
返回值:	如果没有错误返回指向内存块的指针;
		如果有错误返回空。
*********************************************************************************************************
*/

void  *OSMemGet (OS_MEM  *pmem,
                 INT8U   *perr)
{
    void      *pblk;/*指向内存块的指针*/
	#if OS_CRITICAL_METHOD == 3u       
		OS_CPU_SR  cpu_sr = 0u;
	#endif

	#ifdef OS_SAFETY_CRITICAL
		if (perr == (INT8U *)0) {
			OS_SAFETY_CRITICAL_EXCEPTION();
		}
	#endif

	#if OS_ARG_CHK_EN > 0u
		if (pmem == (OS_MEM *)0) {              
			*perr = OS_ERR_MEM_INVALID_PMEM;
			return ((void *)0);
		}
	#endif
    OS_ENTER_CRITICAL();/*进入中断*/
    if (pmem->OSMemNFree > 0u) /*有空闲的内存块*/
	{                 
        pblk = pmem->OSMemFreeList;    /*pblk指向空闲的内存块 */
        pmem->OSMemFreeList = *(void **)pblk;/*调整空闲内存列表*/
        pmem->OSMemNFree--;/*空闲内存块数量减1*/
        OS_EXIT_CRITICAL();/*退出中断*/
        *perr = OS_ERR_NONE;
        return (pblk); /* 返回内存块 */
    }
	/*如果没有空闲的内存块*/
    OS_EXIT_CRITICAL();/*退出中断*/
    *perr = OS_ERR_MEM_NO_FREE_BLKS; /*设置错误码*/
    return ((void *)0);  /*返回空*/
}

得到内存分区名称INT8U  OSMemNameGet (OS_MEM   *pmem,INT8U   **pname,INT8U    *perr):

/*$PAGE*/
/*2018/2/23
*********************************************************************************************************
*                                   GET THE NAME OF A MEMORY PARTITION
*				得到内存分区名称
* Description: This function is used to obtain the name assigned to a memory partition.
*描述:该函数是用来获得内存分区的名称
* Arguments  : pmem      is a pointer to the memory partition
*参数:	--pmem:指向内存分区的指针
*              pname     is a pointer to a pointer to an ASCII string that will receive the name of the memory partition.
*		--pname:指向内存分区名称的指针
*              perr      is a pointer to an error code that can contain one of the following values:
*                        OS_ERR_NONE                if the name was copied to 'pname'
*                        OS_ERR_MEM_INVALID_PMEM    if you passed a NULL pointer for 'pmem'
*                        OS_ERR_PNAME_NULL          You passed a NULL pointer for 'pname'
*                        OS_ERR_NAME_GET_ISR        You called this function from an ISR
*		--perr:指向错误码的指针:
			OS_ERR_NONE:名字被复制到了pname;
			OS_ERR_MEM_INVALID_PMEM:pmem为空指针
			OS_ERR_PNAME_NULL:pname为空指针
			OS_ERR_NAME_GET_ISR:从中断服务子程序中调用该函数
* Returns    : The length of the string or 0 if 'pmem' is a NULL pointer.
返回值:如果pname为空指针返回0,否则返回名称的长度
*********************************************************************************************************
*/

#if OS_MEM_NAME_EN > 0u
INT8U  OSMemNameGet (OS_MEM   *pmem,
                     INT8U   **pname,
                     INT8U    *perr)
{
    INT8U      len;/*存储名称的长度*/
	#if OS_CRITICAL_METHOD == 3u       
		OS_CPU_SR  cpu_sr = 0u;
	#endif

	#ifdef OS_SAFETY_CRITICAL
		if (perr == (INT8U *)0) {
			OS_SAFETY_CRITICAL_EXCEPTION();
		}
	#endif

	#if OS_ARG_CHK_EN > 0u
		if (pmem == (OS_MEM *)0) {           
			*perr = OS_ERR_MEM_INVALID_PMEM;
			return (0u);
		}
		if (pname == (INT8U **)0) {                 
			*perr = OS_ERR_PNAME_NULL;
			return (0u);
		}
	#endif
    if (OSIntNesting > 0u) {                     
        *perr = OS_ERR_NAME_GET_ISR;
        return (0u);
    }
    OS_ENTER_CRITICAL();/(进入中断
    *pname = pmem->OSMemName;/*将名称赋给pname*/
    len    = OS_StrLen(*pname);/*求出名称的长度*/
    OS_EXIT_CRITICAL();/*退出中断*/
    *perr  = OS_ERR_NONE;
    return (len);/*返回长度*/
}
#endif

给中断分区设置名称void  OSMemNameSet (OS_MEM  *pmem,INT8U   *pname,INT8U   *perr):(部分)

    OS_ENTER_CRITICAL();/*进入中断*/
    pmem->OSMemName = pname;/*设置名称*/
    OS_EXIT_CRITICAL();/*退出中断*/
    *perr = OS_ERR_NONE;

释放内存块INT8U  OSMemPut (OS_MEM  *pmem, void  *pblk)(部分):

 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);

查询内存分区信息INT8U  OSMemQuery (OS_MEM *pmem,OS_MEM_DATA  *p_mem_data):(部分)

    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;

其实就是进行了最上面两个结构体内容的赋值。

初始化内存分区管理void  OS_MemInit (void):\

/*$PAGE*/
/*2018/2/23
*********************************************************************************************************
*                                    INITIALIZE MEMORY PARTITION MANAGER
*				初始化内存分区管理
* Description : This function is called by uC/OS-II to initialize the memory partition manager.  Your
*               application MUST NOT call this function.
*,描述:该函数由uc/os-ii调用,用来初始化内存分区管理。你的应用程序不能调用该函数。
* Arguments   : none
*参数:无
* Returns     : none
*返回值:无
* Note(s)    : This function is INTERNAL to uC/OS-II and your application should not call it.
注释:该功能为内部函数
*********************************************************************************************************
*/

void  OS_MemInit (void)
{
	#if OS_MAX_MEM_PART == 1u/*如果内存分区只有一个*/
		OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl));   /* 清除内存分区表 */
		OSMemFreeList = (OS_MEM *)&OSMemTbl[0]; /*指针指向空闲列表的开始 */
	#if OS_MEM_NAME_EN > 0u
		OSMemFreeList->OSMemName    = (INT8U *)"?"; /* 名称初始化为未命名 */
	#endif
	#endif

	#if OS_MAX_MEM_PART >= 2u/*如果不止一个分区*/
		OS_MEM  *pmem;/*指向内存分区的指针*/
		INT16U   i;
    OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl));   /*清除内存分区表 */
    for (i = 0u; i < (OS_MAX_MEM_PART - 1u); i++)/*初始化空闲内存分区表*/
	{   
        pmem = &OSMemTbl[i];  /*指向内存控制块(MCB)*/
        pmem->OSMemFreeList = (void *)&OSMemTbl[i + 1u];  /*更新空闲分区列表 */
		#if OS_MEM_NAME_EN > 0u
				pmem->OSMemName  = (INT8U *)(void *)"?";/*名称设为未命名*/
		#endif
    }
    pmem = &OSMemTbl[i];
    pmem->OSMemFreeList = (void *)0;/*初始化最后一个节点*/
	#if OS_MEM_NAME_EN > 0u
		pmem->OSMemName = (INT8U *)(void *)"?";
	#endif

    OSMemFreeList   = &OSMemTbl[0];/* 将空闲列表指针指向内存分区表的首地址*/
#endif
}
#endif  

到这里os_mem.c文件就读完了。

===============================================================

===============================================================

\