资讯详情

UC/OS-II的内存管理OSMemCreate()分析

我们在写应用程序的过程中通常都是采用一个malloc/free系列函数进行内存的管理,这样分配的内存空间是从应用程序的栈空间分配处理,一般而言我们在写程序的过程中要对内存空间进行适时的释放,才不至于导致栈空间的不足,当然这样也会导致内存垃圾的产生,因为不同大小的内存分配因为对齐等原因导致很多的内存不能再使用,进而使得系统的可用内存越来越小,因此在实时操作系统中通常都需要创建自己的内存管理操作。 uc/os-II中的内存管理主要是采用内存分区控制块实现的,具体的实现过程如下:

/* 关于内存控制块的结构体, 用来跟踪每一个内存分区 每一个分区可以分成很多个小的内存块 每一个内存块的大小都是相同的 */ #if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0) typedef struct { /* MEMORY COROL 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 */ } OS_MEM;

基本的实现思想就是将内存分区分解成很多的大小相同的内存块,然后OSMemFreeList将所有的内存块链接起来,但是此处的链接与我们常用的链表存在一定的差别,这也是内存管理中常用的技巧之一,即在当前块的起始地址处存放下一个内存块的地址,这样就能比较快速的实现内存的管理。在uc/os-II的内存管理代码的OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err)函数中存在一些代码难点。特别是强制类型转换的使用,在uc/os-II中我们看见了大量的强制类型转换问题,下面就做一下简要的分析:

OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err) { #if OS__METHOD == 3 /* Allocate storage for status register */ OS_CPU_SR cpu_sr; #endif OS_MEM *pmem; INT8U *pblk; void **plink; INT32U i;

/*检测参数的正确性*/ #if OS_ARG_CHK_EN > 0 if (addr == (void *)0) { /* Must pass a valid address for the memory part. */ *err = OS_MEM_INLID_A; return ((OS_MEM *)0); } if (nblks < 2) { /* Must have at least 2 blocks per partition */ *err = OS_MEM_INVALID_BLKS; return ((OS_MEM *)0); } if (blksize < sizeof(void *)) { /* Must contain space for at least a pointer */ *err = OS_MEM_INVALID_SIZE; return ((OS_MEM *)0); } #endif OS_ER_CRICAL(); /*得到空闲的内存控制块*/ 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 */ *err = OS_MEM_INVALID_PART; return ((OS_MEM *)0); } /* 将一个分区的各个内存小块链接起来, 形成一个链表 */ plink = (void **)addr; /* Create linked list of free memory blocks */ /*得到一个固定的*/ pblk = (INT8U *)addr + blksize; for (i = 0; i < (nblks - 1); i++) { /*在当前的地址处保存下一个内存块的地址*/ *plink = (void *)pblk; /*将指针指向下一个内存块*/ plink = (void **)pblk; /*得到第三个内存块的地址*/ pblk = pblk + blksize; } /* 将最后一个内存块的下一个地址设置为NULL; */ *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 */ *err = OS_NO_ERR; return (pmem); }

上面的代码大致的意思就是完成内存块的链接以及内存分区控制单元的初始化操作,但是有几句代码存在一定的理解难度。

plink = (void **)addr; /* Create linked list of free memory blocks */ pblk = (INT8U *)addr + blksize; for (i = 0; i < (nblks - 1); i++) { *plink = (void *)pblk; plink = (void **)pblk; pblk = pblk + blksize; } *plink = (void *)0; /* Last memory block points to NULL */

下面几句代码中存在大量的强制类型转换,我们一句一句的分析,plink = (void **)addr的意思是将传递进来的地址强制转换,原因是因为plink是一个存储在函数栈中的变量,它指向了addr指向的地址,而该地址处将来存储的也是一个地址,因此可以看做二维指针,而addr只是一维指针,因此需要强制类型转换为二维指针。

引用:

pblk = (INT8U *)addr + blksize;这句代码其中暗含了我们对指针加减操作的基本理解,因为在uc/OS-II中是按照字节作为内存块分布的,所以进行了INT8U*的强制类型转换,因为只有这样才能保证addr + blksize的操作是增加多少个字节的数据。因为C语言中指针的加减是与其指向的类型的内存空间密切相关的,比如int * p = 0; p ++;此时的p = 4;而当char *p = 0; p ++; 此时的p = 1;这就说明了指针的加减必须注意数据的类型,而不能直接对void*类型的指针进行加减操作。

*plink = (void *)pblk;因为plink是一个栈中变量,而对plink取进行解引用,实际上就是得到addr的值,但是*plink是一个地址,还是一个指针,因此需要强制类型转换。

plink = (void **)pblk;上面的代码已经分析。

基本的思想就是需要注意常数转换为指针的方式方法:int* p = (int *)0x45342341;int **p = (int **)0x45342341;指针的引入主要就是为了解决内存问题,因此对内存的管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台