复合内存管理系统

  1. Light TCP Proxy开发日志(1) http://blog.chinaunix.net/uid-23629988-id-3390203.html
  2. Light TCP Proxy开发日志(2)之伙伴内存管理系统 http://blog.chinaunix.net/space.php?uid=24774106&do=blog&id=3395431
  3. Light TCP Proxy开发日志(3)之复合内存管理系统 http://blog.chinaunix.net/uid-24774106-id-3419831.html
 

本月初的时候实现了伙伴内存管理系统,这个作品我其实是很喜欢的,因为大约一年的这个时候,我在工作中曾有这种实现伙伴内存系统的需求,但是因为太底层,领导不同意动当时的内存管理系统,当时我也没有付诸行动。伙伴管理系统其实是很高效的,而且灵活,对内存的浪费也是有限的。

    呵呵GFree_Wind从需求出发,认为伙伴不能满足Light TCP Proxy的需求,因为伙伴虽然优美,但是没有任何先验的知识。 我们的Light TCP Proxy某些长度的内存分配会特别的频繁,这些个长度是1792,1024,512,256,128,64,其他的长度出现的比较少,GFree_Wind希望对这些长度的内存分配有更高效的。

    说来惭愧,最近心情不佳,所以一直没有做这个,愧对GFree_Wind的信任。昨天下班后搞了搞,搞出了一个基本的实现:

    1 为六种特殊长度的内存分配,建立UB 内存池。
    2 如果用户分配六种特殊长度中的某一种,先尝试UB内存池分配,失败的情况下去buddy分配。
    3 free的时候首先判断是UB分配的还是buddy分配的。

    这个多了个UB内存系统,UB是啥?单元块。 1792长度的UB,分配10240个,1024长度的UB分配10240个,依此类推。10K个UB要管理,不然怎么知道那个已经分配出去了,那个UB还是free的呢。 采用最原始的方法,双向链表freelist。如果是free的,那么挂在freelist上。如果已经分配出去了,从freelist中删除。用户试图分配,只需要将freelist的next摘下来,就可以用了。


  1. typedef struct UB{
  2.     struct list_head head;
  3.     int idx;
  4. }UB;

  5. typedef struct UB_pool
  6. {
  7.     struct list_head freelist;
  8.     pthread_mutex_t fl_lock;
  9.     int UB_size;
  10.     char* begin;
  11.     char* end;
  12.     int total_num;
  13.     int free_num;

  14. }UB_pool;


  15. int UB_size[UB_TYPES] = {1792,1024,512,256,128,64} ;
  16. int UB_num[UB_TYPES] = {10240,10240,10240,10240,10240,10240};
  17. struct UB_pool g_UBPool[UB_TYPES] = {0};
  18. struct UB* p_UB[UB_TYPES] ;
     先看下UB内存池的初始化:

  1. int init_UB_pool()
  2. {
  3.     int i = 0;
  4.     int j = 0;
  5.     memset(g_UBPool,0,UB_TYPES*sizeof(struct UB_pool));
  6.     memset(p_UB,0,UB_TYPES*sizeof(struct UB*));
  7.     for(i = 0;i<UB_TYPES;i++)
  8.     {
  9.          INIT_LIST_HEAD(&(g_UBPool[i].freelist));
  10.          pthread_mutex_init(&g_UBPool[i].fl_lock,NULL);
  11.          g_UBPool[i].UB_size = UB_size[i];
  12.          g_UBPool[i].total_num = UB_num[i];
  13.          g_UBPool[i].free_num = UB_num[i];
  14.          g_UBPool[i].begin = (char*)malloc(UB_size[i]*UB_num[i]); 
  15.          if(g_UBPool[i].begin == NULL)
  16.          {
  17.              goto err_out;
  18.          }
  19.          g_UBPool[i].end = g_UBPool[i].begin + UB_size[i]*UB_num[i];
  20.          p_UB[i] = (struct UB*) malloc(UB_num[i]*sizeof(UB));
  21.          if(p_UB[i] == NULL)
  22.          {
  23.              goto err_out;
  24.          }

  25.          for(j = 0;j<UB_num[i]; j++)
  26.          {
  27.              list_add_tail(&(p_UB[i][j].head),&g_UBPool[i].freelist);
  28.              p_UB[i][j].idx = j;
  29.          }
  30.     }
  31.     return 0;
  32. err_out:
  33.     for(;i >= 0;i--)
  34.     {
  35.         if(p_UB[i])
  36.             free(p_UB[i]);
  37.         if(g_UBPool[i].begin)
  38.             free(g_UBPool[i].begin);
  39.     }
  40.     return -1;
  41. }
     六种型号,为每种长度分配一个内存池。 begin 和end是批发来的内存的起始位置和终止位置,将来free的时候,要靠这个来判断要释放的内存落在那个UB内存池内。分配10240个UB来管理10240个内存块,将他们都挂在freelist上,表示都可以用来分配。


    看下malloc的逻辑。按照方案先判断是否是特殊长度的内存,是,走UB,否则走buddy。 如果UB失败了话,可以继续去buddy分配,buddy失败了,才是真的失败了。


  1. void * MALLOC(int size)
  2. {
  3.     int idx = -1;
  4.     int j;
  5.     for(j = 0 ;j < UB_TYPES;j++)
  6.     {
  7.         if(size == UB_size[j])
  8.         {
  9.             idx = j;
  10.             break;
  11.         }
  12.     }
  13.     if(idx == -1)
  14.     {
  15.         return (void *) buddy_malloc(g_buddypool,size);
  16.     }
  17.     else
  18.     {
  19.         void* ret_ptr = UB_malloc(size,idx);
  20.         if(ret_ptr == NULL)
  21.         {
  22.             return buddy_malloc(g_buddypool,size);
  23.         } 
  24.         else
  25.             return ret_ptr;
  26.     }
  27. }
     buddy内存系统上一篇博文讲过了,我们看下UB_malloc

  1. void* UB_malloc(int size,int idx)
  2. {
  3.     assert(idx >= 0 && idx < UB_TYPES);
  4.     
  5.     pthread_mutex_lock(&g_UBPool[idx].fl_lock);
  6.     
  7.     if(list_empty(&(g_UBPool[idx].freelist)))
  8.     {
  9.         return NULL;
  10.     }

  11.     struct UB* ub =(UB*) (g_UBPool[idx].freelist.next);
  12.     list_del_init((g_UBPool[idx].freelist.next));
  13.     g_UBPool[idx].free_num--;

  14.     pthread_mutex_unlock(&g_UBPool[idx].fl_lock);
  15.     
  16.     int j = ub->idx;
  17.     assert(j >= 0 && j <UB_num[idx]);

  18.     return g_UBPool[idx].begin+j*g_UBPool[idx].UB_size;

  19. }
     没啥好说的,UB里面有个idx,表示的是,我是该长度的第几个内存块,加上起始地址begin,就能确定出return的指针。

   
    因为我们有两个内存管理系统,所以free的时候需要判断是那个内存管理系统分配的。办法就是看落在那个UB池的范围之内。如果都不是,就是buddy分配的。

  1. void FREE(void* ptr)
  2. {
  3.     int idx = -1;
  4.     int j = 0; 

  5.     assert(ptr != NULL);

  6.     for(j = 0;j<UB_TYPES;j++)
  7.     {
  8.         if((unsigned long)ptr >= (unsigned long)g_UBPool[j].begin && (unsigned long)ptr < (unsigned long )g_UBPool[j].end )
  9.         {
  10.             idx = j;
  11.             break;
  12.         }
  13.     }
  14.     if(idx == -1)
  15.     {
  16.         buddy_free(g_buddypool,ptr);
  17.     }
  18.     else
  19.     {
  20.         UB_free(ptr,idx);
  21.     }
  22. }

  1. void UB_free(void* ptr,int idx)
  2. {
  3.     assert(idx >=0 && idx < UB_TYPES);
  4.     assert(((unsigned long)ptr -(unsigned long) g_UBPool[idx].begin)%g_UBPool[idx].UB_size == 0);

  5.     int j = ((unsigned long)ptr -(unsigned long) g_UBPool[idx].begin)/g_UBPool[idx].UB_size;
  6.     assert(j < UB_num[idx]);

  7.     struct UB* ub =&(p_UB[idx][j]);
  8.     pthread_mutex_lock(&g_UBPool[idx].fl_lock);
  9.     list_add_tail(&(ub->head),&g_UBPool[idx].freelist);
  10.     g_UBPool[idx].free_num++;
  11.     pthread_mutex_unlock(&g_UBPool[idx].fl_lock);

  12. }
     代码中有很多的assert,为了就是异常情况出现的时候,能够及时的探知。另外提供了一个UB_check的函数,来检查freelist的链表UB的个数与free_num是否一致。

  1. int UB_check()
  2. {

  3.     int count;
  4.     struct list_head* next = NULL;
  5.     int idx,j;
  6.     for(idx = 0; idx< UB_TYPES;idx++)
  7.     {
  8.         count = 0;
  9.         
  10.         pthread_mutex_lock(&g_UBPool[idx].fl_lock);

  11.         next = g_UBPool[idx].freelist.next;
  12.         while(next != &(g_UBPool[idx].freelist))
  13.         {
  14.             count++;
  15.             next = next->next;
  16.         }

  17.         pthread_mutex_unlock(&g_UBPool[idx].fl_lock);
  18.         if(count != g_UBPool[idx].free_num)
  19.         {
  20.            fprintf(stderr,"UB pool corrupted ,idx = %d\n,free_num = %d, but ub in free list is %d\n",idx,g_UBPool[idx].free_num,count);
  21.         }
  22.     }
  23. }
    最后是测试用例的编写。比较长,我就不贴了。对代码感兴趣的筒子可以去https://github.com/manuscola/MM下down一份代码,自己慢慢看。


很不错的伙伴内存管理系统方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值