详解UCOS中的内存管理

详解UCOS中的内存管理

在嵌入式设备中,持续的调用malloc()和free()容易产生内存碎片,长时间的运行最终会导致内存消耗殆尽。UCOS提供了一套内存管理机制,在系统初始化的时候就分配好内存空间,将所有可用的空间组织成链表,需要申请内存的时候直接从链表中申请,释放内存的时候直接将内存归还到空余内存链表中即可。使用这种方法不仅避免了内存碎片的产生,而且使得在常数时间内分配内存空间成为可能。
UCOS中内存管理的结构体是OS_MEM,具体结构如下:
[cpp]  view plain  copy
 print ?
  1. typedef struct os_mem      //内存控制块  
  2. {                       
  3.     void   *OSMemAddr;     //指向内存分区的首地址  
  4.     void   *OSMemFreeList; //该内存分区的block链表的表头  
  5.     INT32U  OSMemBlkSize;  //每一个block的大小  
  6.     INT32U  OSMemNBlks;    //该分区中block的数目  
  7.     INT32U  OSMemNFree;    //该分区中空闲block的数目  
  8. } 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中创建内存分区的核心代码如下(代码取自OSMemCreate):
[cpp]  view plain  copy
 print ?
  1. //内存分区的首地址  
  2. plink = (void **)addr;      
  3. //第二个block的地址                
  4. pblk  = (INT8U *)((INT32U)addr + blksize);       
  5. for (i = 0; i < (nblks - 1); i++)                                 
  6.   
  7.  //每一个block的开头存放下一个block的首地址  
  8.    *plink = (void *)pblk;      
  9.     //更新指针而已  
  10.     plink = (void **)pblk;                    
  11.     pblk  = (INT8U *)((INT32U)pblk + blksize);     
  12. }  
  13. //最后一个block指向NULL  
  14. *plink              = (void *)0;     
上述代码的核心就在于*pblink = (void*)pblk;每一个block开头存放下一个block的地址,这样便把所有的block组织成了一条链表。

申请一个内存块:
[cpp]  view plain  copy
 print ?
  1. //还有内存可以分配  
  2. if (pmem->OSMemNFree > 0)              
  3. {             
  4. 取一个block  
  5.     pblk                = pmem->OSMemFreeList;      
  6.       
  7.     //使链表表头指向这个block的下一个block  
  8.     pmem->OSMemFreeList = *(void **)pblk;       
  9.     //更新内存块的数量    
  10.     pmem->OSMemNFree--;                        
  11. }  
代码的核心就在于pmem->OSMemFreeList = *(void **)pblk;  因为pblk指向该block,该block的首地址存放的是下一个block的地址,所以这句实际上让空余链表的表头指向了下一个block。

删除一个内存块:
[cpp]  view plain  copy
 print ?
  1. //在将要删除的内存块的首地址存放block表头的地址  
  2. *(void **)pblk      = pmem->OSMemFreeList;  
  3.   
  4. //更新链表表头  
  5. pmem->OSMemFreeList = pblk;      
  6.   
  7. //更新可用内存块的数目    
  8. pmem->OSMemNFree++;     
代码的核心就是*(void **)pblk      = pmem->OSMemFreeList;在将要删除的block的开头存放链表的表头,即相当于把这个block链接到了空余链表的表头中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值