malloc & free

  1. CosOS内核中使用一个内核堆来管理内存,内核通过kmalloc和kfree从内核堆中申请和释放内存。CosOS为用户态编写的库函数中也实现了用户态堆,应用程序通过malloc和free从堆中申请释放内存。  
  2.   
  3.        内核堆和用户态堆的算法类似,都通过调用alloc和free_int来操作堆。下面以代码和注释的形式详细介绍CosOS中 alloc和free_int这两个函数的实现。  
  4.   
  5.   
  6.   
  7.   
  8. 在堆中,可以用的空闲内存块成为hole,堆使用了一个链表来保存堆中所有可用的hole。Alloc函数就是在这个链表中查找到一个大小合适的hole然后根据请求分配的大小把hole切分成两部分,一部分返回给调用者使用,剩下的重新加入到链表中。  
  9.   
  10.   
  11. /*    alloc两个参数,size代表请求分配内存的大小,page_align代表请求内存空间的首地址是否必须页对齐,即地址是能被0x1000整除的。实现这个特性是因为内核中有些空间必须页对齐,例如页表、也目录。*/  
  12.   
  13. void *alloc(u32int size, u8int page_align)  
  14.   
  15.   
  16.   
  17. {  
  18.   
  19.   
  20.   
  21.          register struct hole *hp, *prev_ptr;  
  22.   
  23.   
  24.   
  25.          u32int old_base;  
  26.   
  27.   
  28.   
  29.          prev_ptr = NIL_HOLE;  
  30.   
  31.   
  32.   
  33.          hp = hole_head;  
  34.   
  35.   
  36.   
  37. //通过变量hole链表,查找一个合适的hole   
  38.   
  39.   
  40.   
  41. while(hp != NIL_HOLE)   
  42.   
  43.   
  44.   
  45.          {  
  46.   
  47.   
  48.   
  49.                    if(page_align)  
  50.   
  51.   
  52.   
  53.                    {  
  54.   
  55.   
  56.   
  57. //由于页数限制,页对齐实现部分代码省略。   
  58.   
  59.   
  60.   
  61.                    }else if (hp->h_len >= size)   
  62.   
  63.   
  64.   
  65.                    {  
  66.   
  67.   
  68.   
  69. //找到了一个足够大的hole,使用这个hole。   
  70.   
  71.   
  72.   
  73. //将其地址保存在临时变量old_base中,作为alloc的返回值,   
  74.   
  75.   
  76.   
  77.                             old_base =hp->h_base;     
  78.   
  79.   
  80.   
  81. //由于hole比请求的空间大,剩余部分需放入堆中。只需重新调整hole的首地址和长度即可  
  82.   
  83.   
  84.   
  85.                             hp->h_base +=size;   
  86.   
  87.   
  88.   
  89.                             hp->h_len -=size;  
  90.   
  91.   
  92.   
  93.   
  94.   
  95.   
  96. //记住使用使用过的内存当中,最高的地址,保存在high_watermark当中   
  97.   
  98.   
  99.   
  100.                             if(hp->h_base > high_watermark)  
  101.   
  102.   
  103.   
  104.                                      high_watermark= hp->h_base;  
  105.   
  106.   
  107.   
  108. //经过上面代码调整hole的大小之后,如果hole的大小为0,即hole的大小与请求大小相同,则将其从hole链表中移除。  
  109.   
  110.   
  111.   
  112.                             if (hp->h_len == 0)   
  113.   
  114.   
  115.   
  116.                                      del_slot(prev_ptr,hp);  
  117.   
  118.   
  119.   
  120. //返回分配内存块得首地址。   
  121.   
  122.   
  123.   
  124.                             return (void*)(old_base);  
  125.   
  126.   
  127.   
  128.                    }  
  129.   
  130.   
  131.   
  132.                    prev_ptr = hp;  
  133.   
  134.   
  135.   
  136.                    hp = hp->h_next;  
  137.   
  138.   
  139.   
  140.          }  
  141.   
  142.   
  143.   
  144. //若运行到这里,说明堆中没有合适的内存块,返回一个空指针。   
  145.   
  146.   
  147.   
  148.          return0;  
  149.   
  150.   
  151.   
  152. }  
  153.   
  154.   
  155.   
  156. free_int用于将首地址为base,长度为clicks的内存块释放,重新加入到堆的hole列表中。  
  157.   
  158.   
  159.   
  160. void free_int(u32int base, u32int clicks)  
  161.   
  162.   
  163.   
  164. {  
  165.   
  166.   
  167.   
  168.   struct hole*hp, *new_ptr, *prev_ptr;  
  169.   
  170.   
  171.   
  172. //如果要释放的内存块大小为0,则直接返回   
  173.   
  174.   
  175.   
  176.   if (clicks ==0)   
  177.   
  178.   
  179.   
  180.           return;  
  181.   
  182.   
  183.   
  184.   if ( (new_ptr= free_slots) == NIL_HOLE)   
  185.   
  186.   
  187.   
  188.        ASSERT(0);  
  189.   
  190.   
  191.   
  192. //将内存块设置成一个hole   
  193.   
  194.   
  195.   
  196. new_ptr->h_base = base;  
  197.   
  198.   
  199.   
  200.   new_ptr->h_len = clicks;  
  201.   
  202.   
  203.   
  204.   free_slots = new_ptr->h_next;  
  205.   
  206.   
  207.   
  208.   hp = hole_head;  
  209.   
  210.   
  211.   
  212.   
  213.   
  214.   
  215. //如果内存块得地址是当前hole链表中最小的或者hole链表为空,则把它插入到链表的第一个位置。  
  216.   
  217.   if (hp ==NIL_HOLE || base <= hp->h_base) {  
  218.   
  219.   
  220.   
  221. //插入到第一个位置   
  222.   
  223.   
  224.   
  225.          new_ptr->h_next = hp;  
  226.   
  227.   
  228.   
  229.          hole_head = new_ptr;  
  230.   
  231.   
  232.   
  233. //检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。   
  234.   
  235.   
  236.   
  237. merge(new_ptr);  
  238.   
  239.   
  240.   
  241.          return;  
  242.   
  243.   
  244.   
  245.   }  
  246.   
  247.   
  248.   
  249. //hole没有被加入到首位置,则执行下面代码   
  250.   
  251.   
  252.   
  253.   prev_ptr = NIL_HOLE;  
  254.   
  255.   
  256.   
  257. //hole链表中的hole是按照内存块首地址由低到高排序的,插入新的hole时必须保证插入之后链表还是有序的。因此需要找到一个合适的位置插入。  
  258.   
  259.   
  260.   
  261.   while (hp !=NIL_HOLE && base > hp->h_base) {  
  262.   
  263.   
  264.   
  265.          prev_ptr = hp;  
  266.   
  267.   
  268.   
  269.          hp = hp->h_next;  
  270.   
  271.   
  272.   
  273.   }  
  274.   
  275.   
  276.   
  277. //找到了合适的位置,插入其中   
  278.   
  279.   
  280.   
  281.   new_ptr->h_next = prev_ptr->h_next;  
  282.   
  283.   
  284.   
  285.   prev_ptr->h_next = new_ptr;  
  286.   
  287.   
  288.   
  289. //检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。   
  290.   
  291.   
  292.   
  293.   merge(prev_ptr);     
  294.   
  295.   
  296.   
  297. }  
  298.   
  299.   
  300.   
  301.   
  302.   
  303.   
  304. Hole合并的实现较为简单,只需检测hole的base加上size是否与下一hole的base相同即可。这里不详细介绍  
  305.   
  306.   
  307.   
  308. alloc和free_int是最核心最原始的两个函数。free_int使用时第二个参数需要确定释放内存的大小,而用户态的malloc、free中,free是不需要知道内存大小,只需传入需要释放的地址即可。  
  309.   
  310.   
  311.   
  312. 为了在使用free时无需知道内存块得大小也可以正确释放内存块,CosOS中采用的方法是,将内存块得大小保存在内存块首地址前得一个整型空间中,具体实现如下:  
  313.   
  314.   
  315.   
  316. //size为申请内存的大小   
  317.   
  318.   
  319.   
  320. void * malloc(u32int size)  
  321.   
  322.   
  323.   
  324. {  
  325.   
  326.   
  327.   
  328. //使用alloc从堆中申请内存时,多申请4个字节,用来保存内存块大小。   
  329.   
  330.   
  331.   
  332.          void *p= alloc(size + 4);  
  333.   
  334.   
  335.   
  336. //在返回内存块的前4个字节中(即一个整型中)保存内存块大小。   
  337.   
  338.   
  339.   
  340.          *((u32int*)p) = size;  
  341.   
  342.   
  343.   
  344. //返回这4个字节之后的地址,防止使用内存块时,大小被破坏。   
  345.   
  346.   
  347.   
  348.          return(void*)((u32int)p + 4);  
  349.   
  350.   
  351.   
  352. }  
  353.   
  354.   
  355.   
  356. void free(void *p)  
  357.   
  358.   
  359.   
  360. {  
  361.   
  362.   
  363.   
  364. //从内存块首地址的前4个字节中获取内存块的大小,保存在size中。   
  365.   
  366.   
  367.   
  368.          u32int size = *((u32int*)p - 1);  
  369.   
  370.   
  371.   
  372. //使用free_int释放这块内存。   
  373.   
  374.   
  375.   
  376.          free_int((u32int)p - 4, size + 4);  
  377.   
  378.   
  379.   
  380. }  
  381.   
  382.    
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);



}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值