- Light TCP Proxy开发日志(1) http://blog.chinaunix.net/uid-23629988-id-3390203.html
- Light TCP Proxy开发日志(2)之伙伴内存管理系统 http://blog.chinaunix.net/space.php?uid=24774106&do=blog&id=3395431
- 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摘下来,就可以用了。
- typedef struct UB{
- struct list_head head;
- int idx;
- }UB;
- typedef struct UB_pool
- {
- struct list_head freelist;
- pthread_mutex_t fl_lock;
- int UB_size;
- char* begin;
- char* end;
- int total_num;
- int free_num;
- }UB_pool;
- int UB_size[UB_TYPES] = {1792,1024,512,256,128,64} ;
- int UB_num[UB_TYPES] = {10240,10240,10240,10240,10240,10240};
- struct UB_pool g_UBPool[UB_TYPES] = {0};
- struct UB* p_UB[UB_TYPES] ;
- int init_UB_pool()
- {
- int i = 0;
- int j = 0;
- memset(g_UBPool,0,UB_TYPES*sizeof(struct UB_pool));
- memset(p_UB,0,UB_TYPES*sizeof(struct UB*));
- for(i = 0;i<UB_TYPES;i++)
- {
- INIT_LIST_HEAD(&(g_UBPool[i].freelist));
- pthread_mutex_init(&g_UBPool[i].fl_lock,NULL);
- g_UBPool[i].UB_size = UB_size[i];
- g_UBPool[i].total_num = UB_num[i];
- g_UBPool[i].free_num = UB_num[i];
- g_UBPool[i].begin = (char*)malloc(UB_size[i]*UB_num[i]);
- if(g_UBPool[i].begin == NULL)
- {
- goto err_out;
- }
- g_UBPool[i].end = g_UBPool[i].begin + UB_size[i]*UB_num[i];
- p_UB[i] = (struct UB*) malloc(UB_num[i]*sizeof(UB));
- if(p_UB[i] == NULL)
- {
- goto err_out;
- }
- for(j = 0;j<UB_num[i]; j++)
- {
- list_add_tail(&(p_UB[i][j].head),&g_UBPool[i].freelist);
- p_UB[i][j].idx = j;
- }
- }
- return 0;
- err_out:
- for(;i >= 0;i--)
- {
- if(p_UB[i])
- free(p_UB[i]);
- if(g_UBPool[i].begin)
- free(g_UBPool[i].begin);
- }
- return -1;
- }
看下malloc的逻辑。按照方案先判断是否是特殊长度的内存,是,走UB,否则走buddy。 如果UB失败了话,可以继续去buddy分配,buddy失败了,才是真的失败了。
- void * MALLOC(int size)
- {
- int idx = -1;
- int j;
- for(j = 0 ;j < UB_TYPES;j++)
- {
- if(size == UB_size[j])
- {
- idx = j;
- break;
- }
- }
- if(idx == -1)
- {
- return (void *) buddy_malloc(g_buddypool,size);
- }
- else
- {
- void* ret_ptr = UB_malloc(size,idx);
- if(ret_ptr == NULL)
- {
- return buddy_malloc(g_buddypool,size);
- }
- else
- return ret_ptr;
- }
- }
- void* UB_malloc(int size,int idx)
- {
- assert(idx >= 0 && idx < UB_TYPES);
-
- pthread_mutex_lock(&g_UBPool[idx].fl_lock);
-
- if(list_empty(&(g_UBPool[idx].freelist)))
- {
- return NULL;
- }
- struct UB* ub =(UB*) (g_UBPool[idx].freelist.next);
- list_del_init((g_UBPool[idx].freelist.next));
- g_UBPool[idx].free_num--;
- pthread_mutex_unlock(&g_UBPool[idx].fl_lock);
-
- int j = ub->idx;
- assert(j >= 0 && j <UB_num[idx]);
- return g_UBPool[idx].begin+j*g_UBPool[idx].UB_size;
- }
因为我们有两个内存管理系统,所以free的时候需要判断是那个内存管理系统分配的。办法就是看落在那个UB池的范围之内。如果都不是,就是buddy分配的。
- void FREE(void* ptr)
- {
- int idx = -1;
- int j = 0;
- assert(ptr != NULL);
- for(j = 0;j<UB_TYPES;j++)
- {
- if((unsigned long)ptr >= (unsigned long)g_UBPool[j].begin && (unsigned long)ptr < (unsigned long )g_UBPool[j].end )
- {
- idx = j;
- break;
- }
- }
- if(idx == -1)
- {
- buddy_free(g_buddypool,ptr);
- }
- else
- {
- UB_free(ptr,idx);
- }
- }
- void UB_free(void* ptr,int idx)
- {
- assert(idx >=0 && idx < UB_TYPES);
- assert(((unsigned long)ptr -(unsigned long) g_UBPool[idx].begin)%g_UBPool[idx].UB_size == 0);
- int j = ((unsigned long)ptr -(unsigned long) g_UBPool[idx].begin)/g_UBPool[idx].UB_size;
- assert(j < UB_num[idx]);
- struct UB* ub =&(p_UB[idx][j]);
- pthread_mutex_lock(&g_UBPool[idx].fl_lock);
- list_add_tail(&(ub->head),&g_UBPool[idx].freelist);
- g_UBPool[idx].free_num++;
- pthread_mutex_unlock(&g_UBPool[idx].fl_lock);
- }
- int UB_check()
- {
- int count;
- struct list_head* next = NULL;
- int idx,j;
- for(idx = 0; idx< UB_TYPES;idx++)
- {
- count = 0;
-
- pthread_mutex_lock(&g_UBPool[idx].fl_lock);
- next = g_UBPool[idx].freelist.next;
- while(next != &(g_UBPool[idx].freelist))
- {
- count++;
- next = next->next;
- }
- pthread_mutex_unlock(&g_UBPool[idx].fl_lock);
- if(count != g_UBPool[idx].free_num)
- {
- 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);
- }
- }
- }
很不错的伙伴内存管理系统方案。