> 上文主要介绍了邮箱管理相关的函数,本文介绍内存管理相关的函数:OSMemCreate()内存块创建函数,OSMemGet()函数,OSMemPut()函数,OSMemQuery()函数.以前用过的uC/OS-II笔记分享到这里。
在ANSI C中可以用malloc()和free()两个函数动态地分配内存和释放内存。但是,在嵌入式实时操作系统中,多次这样做会把原来很大的一块连续内存区域,逐渐地分割成许多非常小而且彼此又不相邻的内存区域,也就是内存碎片。由于这些碎片的大量存在,使得程序到后来连非常小的内存也分配不到。另外,由于内存管理算法的原因,malloc()和free()函数执行时间是不确定的。 在μC/OS-II中,操作系统把连续的大块内存按分区来管理。每个分区中包含有整数个大小相同的内存块。利用这种机制,μC/OS-II 对malloc()和free()函数进行了改进,使得它们可以分配和释放固定大小的内存块。这样一来,malloc()和free()函数的执行时间也是固定的了。 在一个系统中可以有多个内存分区。这样,用户的应用程序就可以从不同的内存分区中得到不同大小的内存块。但是,特定的内存块在释放时必须重新放回它以前所属于的内存分区。显然,采用这样的内存管理算法,上面的内存碎片问题就得到了解决。
为了便于内存的管理,在μC/OS-II中使用内存控制块(memory control blocks)的数据结构来跟踪每一个内存分区,系统中的每个内存分区都有它自己的内存控制块。 内存控制块的定义如下: typedef struct { void *OSMemAddr; //指向内存分区起始地址的指针 void *OSMemFreeList; //是指向下一个空闲内存控制块或者下一个空闲的内存块的指针 INT32U OSMemBlkSize; //是内存分区中内存块的大小 INT32U OSMemNBlks; //内存分区中总的内存块数量 INT32U OSMemNFree; //内存分区中当前可以得空闲内存块数量 } OS_MEM; 如果要在μC/OS-II中使用内存管理,需要在OS_CFG.H文件中将开关量OS_MEM_EN设置为1。这样μC/OS-II 在启动时就会对内存管理器进行初始化[由OSInit()调用OSMemInit()实现]。常数OS_MAX_MEM_PART(见文件OS_CFG.H)定义了最大的内存分区数,该常数值最小应为2。
使用内存管理模块需要做的工作还有: 1.打开配置文件OS_CFG.H,将开关量OS_MEM_EN设置为1: #define OS_MEM_EN 0 2.打开配置文件OS_CFG.H,设置系统要建立的任务分区的数量: #define OS_MAX_MEM_PART 2
1 主要作用: 该函数建立并初始化一个用于动态内存分配的区域,该内存区域包含指定数目的、大小确定的内存块。应用可以动态申请这些内存块并在用完后将其释放回这个内存区域。该函数的返回值就是指向这个内存区域控制块的指针,并作为OSMemGet(),OSMemPut(),OSMemQuery() 等相关调用的参数。 2函数原型:OS_MEM *OSMemCreate( void *addr, INT32U nblks, INT32U blksize, INT8U *err ); 3参数说明:addr 建立的内存区域的起始地址。可以使用静态数组或在系统初始化时使用 malloc() 函数来分配这个区域的空间。 nblks 内存块的数目。每一个内存区域最少需要定义两个内存块。 blksize 每个内存块的大小,最小应该能够容纳一个指针变量。 err 是指向包含错误码的变量的指针。Err可能是如下几种情况: OS_NO_ERR :成功建立内存区域。 OS_MEM_INVALID_ADDR :非法地址,即地址为空指针。 OS_MEM_INVALID_PART :没有空闲的内存区域。 OS_MEM_INVALID_BLKS :没有为内存区域建立至少两个内存块。 OS_MEM_INVALID_SIZE :内存块大小不足以容纳一个指针变量。 5、函数主体在os_men.c中 4返回值说明: OSMemCreate() 函数返回指向所创建的内存区域控制块的指针。如果创建失败,函数返回空指针。
1 主要作用: 该函数用于从内存区域分配一个内存块。用户程序必须知道所建立的内存块的大小,并必须在使用完内存块后释放它。可以多次调用 OSMemGet() 函数。它的返回值就是指向所分配内存块的指针,并作为 OSMemPut() 函数的参数。 2函数原型:void *OSMemGet(OS_MEM *pmem, INT8U *err); 3参数说明:pmem 是指向内存区域控制块的指针,可以从 OSMemCreate() 函数的返回值中得到。 err 是指向包含错误码的变量的指针。Err可能是如下情况: OS_NO_ERR :成功得到一个内存块。 OS_MEM_NO_FREE_BLKS :内存区域中已经没有足够的内存块。 4返回值说明: OSMemGet() 函数返回指向所分配内存块的指针。如果没有可分配的内存块,OSMemGet() 函数返回空指针。 5、函数主体在os_men.c中
1 主要作用:该函数用于释放一个内存块,内存块必须释放回它原先所在的内存区域,否则会造成系统错误。 2函数原型:INT8U OSMemPut (OS_MEM *pmem, void *pblk); 3参数说明:pmem 是指向内存区域控制块的指针,可以从 OSMemCreate() 函数的返回值中得到。 pblk 是指向将被释放的内存块的指针。 4返回值说明: OSMemPut() 函数的返回值为下述之一: OS_NO_ERR :成功释放内存块 OS_MEM_FULL :内存区域已满,不能再接受更多释放的内存块。这种情况说明用户程序出现了错误,释放了多于用 OSMemGet() 函数得到的内存块。 5、函数主体在os_men.c中
1 主要作用:该函数用于得到内存区域的信息。 2函数原型:INT8U OSMemQuery(OS_MEM *pmem, OS_MEM_DATA pdata); 3参数说明:pmem 是指向内存区域控制块的指针,可以从 OSMemCreate() 函数的返回值中得到。 pdata 是一个指向 OS_MEM_DATA 数据结构的指针,该数据结构包含了以下的域: void OSAddr; / 指向内存区域起始地址的指针 / void OSFreeList; / 指向空闲内存块列表起始地址的指针 / INT32U OSBlkSize; / 每个内存块的大小 / INT32U OSNBlks; / 该内存区域中的内存块总数 / INT32U OSNFree; / 空闲的内存块数目 / INT32U OSNUsed; / 已使用的内存块数目 */ 4返回值说明:函数返回值总是OS_NO_ERR。 5、函数主体在os_men.c中
/* ********************************************************************************************************* * uC/OS-II * The Real-Time Kernel * MEMORY MANAGEMENT * * (c) Copyright 1992-2013, Micrium, Weston, FL * All Rights Reserved * * File : OS_MEM.C * By : Jean J. Labrosse * Version : V2.92.08 * * LICENSING TERMS: * --------------- * uC/OS-II is provided in source form for FREE evaluation, for educational use or for peaceful research. * If you plan on using uC/OS-II in a commercial product you need to contact Micrium to properly license * its use in your product. We provide ALL the source code for your convenience and to help you experience * uC/OS-II. The fact that the source is provided does NOT mean that you can use it without paying a * licensing fee. ********************************************************************************************************* */ # define MICRIUM_SOURCE # ifndef OS_MASTER_FILE # include <ucos_ii.h> # endif # if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u) /* ********************************************************************************************************* * CREATE A MEMORY PARTITION * * Description : Create a fixed-sized memory partition that will be managed by uC/OS-II. * * Arguments : addr is the starting address of the memory partition * * 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. * * 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 * 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 *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 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; # endif # ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return ((OS_MEM *)0); } # endif # ifdef OS_SAFETY_CRITICAL_IEC61508 if (OSSafetyCriticalStartFlag == OS_TRUE) { OS_SAFETY_CRITICAL_EXCEPTION(); return ((OS_MEM *)0); } # endif # if OS_ARG_CHK_EN > 0u if (addr == (void *)0) { /* Must pass a valid address for the memory part.*/ *perr = OS_ERR_MEM_INVALID_ADDR; return ((OS_MEM *)0); } if (((INT32U)addr & (sizeof(void *) - 1u)) != 0u){ /* Must be pointer size aligned */ *perr = OS_ERR_MEM_INVALID_ADDR; return ((OS_MEM *)0); } if (nblks < 2u) { /* Must have at least 2 blocks per partition */ *perr = OS_ERR_MEM_INVALID_BLKS; return ((OS_MEM *)0); } if (blksize < sizeof(void *)) { /* Must contain space for at least a pointer */ *perr = OS_ERR_MEM_INVALID_SIZE; return ((OS_MEM *)0); } # endif OS_ENTER_CRITICAL(); pmem = OSMemFreeList; /* Get next free memory partition */ if (OSMemFreeList != (OS_MEM *)0) { /* See if pool of free partitions was empty */ OSMemFreeList = (OS_MEM *)OSMemFreeList-> OSMemFreeList; } OS_EXIT_CRITICAL(); if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */ *perr = OS_ERR_MEM_INVALID_PART; return ((OS_MEM *)0); } plink = (void **)addr; /* Create linked list of free memory blocks */ pblk = (INT8U *)addr; loops = nblks - 1u; for (i = 0u; i < loops; i++) { pblk += blksize; /* Point to the FOLLOWING block */ *plink = (void *)pblk; /* Save pointer to NEXT block in CURRENT block */ plink = (void **)pblk; /* Position to NEXT block */ } *plink = (void *)0; /* Last memory block points to NULL */ pmem-> OSMemAddr = addr; /* Store start address of memory partition */ pmem-> OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */ pmem-> OSMemNFree = nblks; /* Store number of free blocks in MCB */ pmem-> OSMemNBlks = nblks; pmem-> OSMemBlkSize = blksize; /* Store block size of each memory blocks */ *perr = OS_ERR_NONE; return (pmem); } /*$PAGE*/ /* ********************************************************************************************************* * GET A MEMORY BLOCK * * Description : Get a memory block from a partition * * Arguments : pmem is a pointer to the memory partition control block * * 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' * * 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 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; # endif # ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return ((void *)0); } # endif # if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ *perr = OS_ERR_MEM_INVALID_PMEM; return ((void *)0); } # endif OS_ENTER_CRITICAL(); if (pmem-> OSMemNFree > 0u) { /* See if there are any free memory blocks */ pblk = pmem-> OSMemFreeList; /* Yes, point to next free memory block */ pmem-> OSMemFreeList = *(void **)pblk; /* Adjust pointer to new free list */ pmem-> OSMemNFree--; /* One less memory block in this partition */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; /* No error */ return (pblk); /* Return memory block to caller */ } OS_EXIT_CRITICAL(); *perr = OS_ERR_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition */ return ((void *)0); /* Return NULL pointer to caller */ } /*$PAGE*/ /* ********************************************************************************************************* * 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 * * pname is a pointer to a pointer to an ASCII string that will receive the name of the memory partition. * * 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 * * Returns : The length of the string or 0 if 'pmem' is a NULL pointer. ********************************************************************************************************* */ # if OS_MEM_NAME_EN > 0u INT8U OSMemNameGet (OS_MEM *pmem, INT8U **pname, INT8U *perr) { INT8U len; # if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; # endif # ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return (0u); } # endif # if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Is 'pmem' a NULL pointer? */ *perr = OS_ERR_MEM_INVALID_PMEM; return (0u); } if (pname == (INT8U **)0) { /* Is 'pname' a NULL pointer? */ *perr = OS_ERR_PNAME_NULL; return (0u); } # endif if (OSIntNesting > 0u) { /* See if trying to call from an ISR */ *perr = OS_ERR_NAME_GET_ISR; return (0u); } OS_ENTER_CRITICAL(); *pname = pmem-> OSMemName; len = OS_StrLen(*pname); OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (len); } # endif /*$PAGE*/ /* ********************************************************************************************************* * ASSIGN A NAME TO A MEMORY PARTITION * * Description: This function assigns a name to a memory partition. * * Arguments : pmem is a pointer to the memory partition * * pname is a pointer to an ASCII string that contains the name of the memory partition. * * 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_MEM_NAME_TOO_LONG if the name doesn't fit in the storage area * OS_ERR_NAME_SET_ISR if you called this function from an ISR * * Returns : None ********************************************************************************************************* */ # if OS_MEM_NAME_EN > 0u void OSMemNameSet (OS_MEM *pmem, INT8U *pname, INT8U *perr) { # if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; # endif # ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return; } # endif # if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Is 'pmem' a NULL pointer? */ *perr = OS_ERR_MEM_INVALID_PMEM; return; } if (pname == (INT8U *)0) { /* Is 'pname' a NULL pointer? */ *perr = OS_ERR_PNAME_NULL; return; } # endif if (OSIntNesting > 0u) { /* See if trying to call from an ISR */ *perr = OS_ERR_NAME_SET_ISR; return; } OS_ENTER_CRITICAL(); pmem-> OSMemName = pname; OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; } # endif /*$PAGE*/ /* ********************************************************************************************************* * RELEASE A MEMORY BLOCK * * Description : Returns a memory block to a partition * * Arguments : pmem is a pointer to the memory partition control block * * pblk is a pointer to the memory block being released. * * Returns : OS_ERR_NONE if the memory block was inserted into the partition * OS_ERR_MEM_FULL if you are returning a memory block to an already FULL memory * partition (You freed more blocks than you allocated!) * OS_ERR_MEM_INVALID_PMEM if you passed a NULL pointer for 'pmem' * OS_ERR_MEM_INVALID_PBLK if you passed a NULL pointer for the block to release. ********************************************************************************************************* */ INT8U OSMemPut (OS_MEM *pmem, void *pblk) { # if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; # endif # if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ return (OS_ERR_MEM_INVALID_PMEM); } if (pblk == (void *)0) { /* Must release a valid block */ return (OS_ERR_MEM_INVALID_PBLK); } # endif OS_ENTER_CRITICAL(); if (pmem-> OSMemNFree > = pmem-> OSMemNBlks) { /* Make sure all blocks not already returned */ OS_EXIT_CRITICAL(); return (OS_ERR_MEM_FULL); } *(void **)pblk = pmem-> OSMemFreeList; /* Insert released block into free block list */ pmem-> OSMemFreeList = pblk; pmem-> OSMemNFree++; /* One more memory block in this partition */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); /* Notify caller that memory block was released */ } /*$PAGE*/ /* ********************************************************************************************************* * QUERY MEMORY PARTITION * * Description : This function is used to determine the number of free memory blocks and the number of * used memory blocks from a memory partition. * * Arguments : pmem is a pointer to the memory partition control block * * p_mem_data is a pointer to a structure that will contain information about the memory * partition. * * Returns : OS_ERR_NONE if no errors were found. * OS_ERR_MEM_INVALID_PMEM if you passed a NULL pointer for 'pmem' * OS_ERR_MEM_INVALID_PDATA if you passed a NULL pointer to the data recipient. ********************************************************************************************************* */ # if OS_MEM_QUERY_EN > 0u INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data) { # if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; # endif # if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ return (OS_ERR_MEM_INVALID_PMEM); } if (p_mem_data == (OS_MEM_DATA *)0) { /* Must release a valid storage area for the data */ 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 /* OS_MEM_QUERY_EN */ /*$PAGE*/ /* ********************************************************************************************************* * 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. * * 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)); /* Clear the memory partition table */ OSMemFreeList = (OS_MEM *)&OSMemTbl[0]; /* Point to beginning of free list */ # if OS_MEM_NAME_EN > 0u OSMemFreeList-> OSMemName = (INT8U *)"?"; /* Unknown name */ # endif # endif # if OS_MAX_MEM_PART > = 2u OS_MEM *pmem; INT16U i; OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl)); /* Clear the memory partition table */ for (i = 0u; i < (OS_MAX_MEM_PART - 1u); i++) { /* Init. list of free memory partitions */ pmem = &OSMemTbl[i]; /* Point to memory control block (MCB) */ pmem-> OSMemFreeList = (void *)&OSMemTbl[i + 1u]; /* Chain list of free partitions */ # if OS_MEM_NAME_EN > 0u pmem-> OSMemName = (INT8U *)(void *)"?"; # endif } pmem = &OSMemTbl[i]; pmem-> OSMemFreeList = (void *)0; /* Initialize last node */ # if OS_MEM_NAME_EN > 0u pmem-> OSMemName = (INT8U *)(void *)"?"; # endif OSMemFreeList = &OSMemTbl[0]; /* Point to beginning of free list */ # endif } # endif /* OS_MEM_EN */