Swift51.com
麦子学院 头像
麦子学院  2016-12-18 22:35

UCOS学习之内存管理

回复:0  查看:2490  

在嵌入式设备中,持续的调用malloc()free()容易产生内存碎片,长时间的运行最终会导致内存消耗殆尽。UCOS系统提供了一套内存管理机制,在系统初始化的时候就分配好内存空间,将所有可用的空间组织成链表,需要申请内存的时候直接从链表中申请,释放内存的时候直接将内存归还到空余内存链表中即可。使用这种方法不仅避免了内存碎片的产生,而且使得在常数时间内分配内存空间成为可能。

UCOS中内存管理的结构体是OS_MEM,具体结构如下:

typedef struct os_mem //内存控制块{

void *OSMemAddr; //指向内存分区的首地址

void *OSMemFreeList; //该内存分区的block链表的表头

INT32U OSMemBlkSize; //每一个block的大小

INT32U OSMemNBlks; //该分区中block的数目

INT32U OSMemNFree; //该分区中空闲block的数目

} OS_MEM;

  在UCOS中,一个内存分区被划分成很多个大小相等的内存块,这些内存块链接成链表,链表的表头存于OSMemFreeList中。

  比如一个有4个内存块,每个内存块128bit的内存分区Memoy如下:

INT32U Memory[4][4]

UCOS的思路是我们将要分配的空间组织成二维数组,然后二维数组的每一行就是一个block,二维数组的列数既是block的大小。

Memory的第一个block的首地址是 Memor[0]

Memory的第二个block的首地址是 Memory[1]

Memory的第三个block的首地址是 Memory[2]

Memory的第四个block的首地址是 Memory[3]

  为了管理方便我们在每一个block的开头存储下一个block的地址,这样就把所有的block串接成了单链表。

  第一个block的开头存储 Memory[1]

  第一个block的开头存储 Memory[2]

  第一个block的开头存储 Memory[3]

  第一个block的开头存储 NULL

  最终结果如下图:

UCOS学习之内存管理

 

UCOS中创建内存分区的核心代码如下(代码取自OSMemCreate)

//内存分区的首地址

plink = (void **)addr;

//第二个block的地址

pblk = (INT8U *)((INT32U)addr + blksize);

for (i = 0; i < (nblks - 1); i++)

{

//每一个block的开头存放下一个block的首地址

*plink = (void *)pblk;

//更新指针而已

plink = (void **)pblk;

pblk = (INT8U *)((INT32U)pblk + blksize);

}

//最后一个block指向NULL

*plink = (void *)0;

  上述代码的核心就在于*pblink = (void*)pblk;每一个block开头存放下一个block的地址,这样便把所有的block组织成了一条链表。

  申请一个内存块:

//还有内存可以分配

if (pmem->OSMemNFree > 0)

{

//获取一个block

pblk = pmem->OSMemFreeList;

//使链表表头指向这个block的下一个block

pmem->OSMemFreeList = *(void **)pblk;

//更新内存块的数量

pmem->OSMemNFree--;

}

  代码的核心就在于pmem->OSMemFreeList = *(void **)pblk; 因为pblk指向该block,该block的首地址存放的是下一个block的地址,所以这句实际上让空余链表的表头指向了下一个block

  删除一个内存块:

//在将要删除的内存块的首地址存放block表头的地址

*(void **)pblk = pmem->OSMemFreeList;

//更新链表表头

pmem->OSMemFreeList = pblk;

//更新可用内存块的数目

pmem->OSMemNFree++;

  代码的核心就是*(void **)pblk = pmem->OSMemFreeList;在将要删除的block的开头存放链表的表头,即相当于把这个block链接到了空余链表的表头中。

 

来源:csdn