malloc & free

 
CosOS内核中使用一个内核堆来管理内存,内核通过kmalloc和kfree从内核堆中申请和释放内存。CosOS为用户态编写的库函数中也实现了用户态堆,应用程序通过malloc和free从堆中申请释放内存。

       内核堆和用户态堆的算法类似,都通过调用alloc和free_int来操作堆。下面以代码和注释的形式详细介绍CosOS中 alloc和free_int这两个函数的实现。




在堆中,可以用的空闲内存块成为hole,堆使用了一个链表来保存堆中所有可用的hole。Alloc函数就是在这个链表中查找到一个大小合适的hole然后根据请求分配的大小把hole切分成两部分,一部分返回给调用者使用,剩下的重新加入到链表中。


/*    alloc两个参数,size代表请求分配内存的大小,page_align代表请求内存空间的首地址是否必须页对齐,即地址是能被0x1000整除的。实现这个特性是因为内核中有些空间必须页对齐,例如页表、也目录。*/

void *alloc(u32int size, u8int page_align)



{



         register struct hole *hp, *prev_ptr;



         u32int old_base;



         prev_ptr = NIL_HOLE;



         hp = hole_head;



//通过变量hole链表,查找一个合适的hole



while(hp != NIL_HOLE) 



         {



                   if(page_align)



                   {



//由于页数限制,页对齐实现部分代码省略。



                   }else if (hp->h_len >= size) 



                   {



//找到了一个足够大的hole,使用这个hole。



//将其地址保存在临时变量old_base中,作为alloc的返回值,



                            old_base =hp->h_base;   



//由于hole比请求的空间大,剩余部分需放入堆中。只需重新调整hole的首地址和长度即可



                            hp->h_base +=size; 



                            hp->h_len -=size;






//记住使用使用过的内存当中,最高的地址,保存在high_watermark当中



                            if(hp->h_base > high_watermark)



                                     high_watermark= hp->h_base;



//经过上面代码调整hole的大小之后,如果hole的大小为0,即hole的大小与请求大小相同,则将其从hole链表中移除。



                            if (hp->h_len == 0) 



                                     del_slot(prev_ptr,hp);



//返回分配内存块得首地址。



                            return (void*)(old_base);



                   }



                   prev_ptr = hp;



                   hp = hp->h_next;



         }



//若运行到这里,说明堆中没有合适的内存块,返回一个空指针。



         return0;



}



free_int用于将首地址为base,长度为clicks的内存块释放,重新加入到堆的hole列表中。



void free_int(u32int base, u32int clicks)



{



  struct hole*hp, *new_ptr, *prev_ptr;



//如果要释放的内存块大小为0,则直接返回



  if (clicks ==0) 



          return;



  if ( (new_ptr= free_slots) == NIL_HOLE) 



       ASSERT(0);



//将内存块设置成一个hole



new_ptr->h_base = base;



  new_ptr->h_len = clicks;



  free_slots = new_ptr->h_next;



  hp = hole_head;






//如果内存块得地址是当前hole链表中最小的或者hole链表为空,则把它插入到链表的第一个位置。

  if (hp ==NIL_HOLE || base <= hp->h_base) {



//插入到第一个位置



         new_ptr->h_next = hp;



         hole_head = new_ptr;



//检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。



merge(new_ptr);



         return;



  }



//hole没有被加入到首位置,则执行下面代码



  prev_ptr = NIL_HOLE;



//hole链表中的hole是按照内存块首地址由低到高排序的,插入新的hole时必须保证插入之后链表还是有序的。因此需要找到一个合适的位置插入。



  while (hp !=NIL_HOLE && base > hp->h_base) {



         prev_ptr = hp;



         hp = hp->h_next;



  }



//找到了合适的位置,插入其中



  new_ptr->h_next = prev_ptr->h_next;



  prev_ptr->h_next = new_ptr;



//检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。



  merge(prev_ptr);   



}






Hole合并的实现较为简单,只需检测hole的base加上size是否与下一hole的base相同即可。这里不详细介绍



alloc和free_int是最核心最原始的两个函数。free_int使用时第二个参数需要确定释放内存的大小,而用户态的malloc、free中,free是不需要知道内存大小,只需传入需要释放的地址即可。



为了在使用free时无需知道内存块得大小也可以正确释放内存块,CosOS中采用的方法是,将内存块得大小保存在内存块首地址前得一个整型空间中,具体实现如下:



//size为申请内存的大小



void * malloc(u32int size)



{



//使用alloc从堆中申请内存时,多申请4个字节,用来保存内存块大小。



         void *p= alloc(size + 4);



//在返回内存块的前4个字节中(即一个整型中)保存内存块大小。



         *((u32int*)p) = size;



//返回这4个字节之后的地址,防止使用内存块时,大小被破坏。



         return(void*)((u32int)p + 4);



}



void free(void *p)



{



//从内存块首地址的前4个字节中获取内存块的大小,保存在size中。



         u32int size = *((u32int*)p - 1);



//使用free_int释放这块内存。



         free_int((u32int)p - 4, size + 4);



}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值