經歷了兩週多吧,總算把Glibc malloc 源碼分析的文檔啃完了,也算是對於malloc這個東西有深入瞭解吧,畢竟本身也寫了兩版代碼,後邊還會出一個多線程版本的。就是在這個版本上修改一個支持多線程的,這個算是V2.0了。
前面已經闡述了malloc的分配思想,這裏我接合線程池原理和邊界標識法,作了這個2.0 版本。 1.最小塊的大小所爲40B。 2.bins 只創建了40,48,56,64總共4個bin,可是一樣支持更大內存分配。 3.分配區只有一個,下一個版本多個分配區,同時開啓支持多線程模式。
一圖勝千言嘛。
這裏大概說明一下,一個主分配區,其中包含一個頭結構負責記錄一些全局的信息,後邊就是bins,free 的分配塊會首先放在這裏,暫時不給操做系統,以留後用,再後邊是咱們分配的主題空間。node
這裏咱們按照8字節對齊,因此每個大小的最後三個位始終爲0,咱們就能夠利用這三個位來作一些標記,咱們設置最後一位爲是否使用的標記位,倒數第二位爲是否從heap分配的空間,若是是就置爲1,不是就爲0。數組
#include<stdio.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#define HAFSIZE (1<<2)
#define SIZE (1<<3)
#define DSIZE (1<<4)
#define MAX_CHUNK 64
#define MIN_CHUNK (32 +8)
#define ALIGN 8
#define TEMP_MEM (1<<21) //2MB
#define SIZE_BITS 0x3
#define GET_SIZE(chunk) ((((mem_chunk *)(chunk))->size_tag)&=(~0x3))
//獲取一個塊的大小
//處理這個塊的使用狀態
#define GET_state(chunk) ((((mem_chunk *)(chunk))->size_tag)&(0x01))
#define SET_use(chunk) ((((mem_chunk *)(chunk))->size_tag)|=(0x01))
#define SET_free(chunk) ((((mem_chunk *)(chunk))->size_tag)&=(~0x01))
//處理是否被heap 分配
#define GET_MAP(chunk) ((((mem_chunk *)(chunk))->size_tag)&(0x02) >> 1)
#define SET_MAP(chunk) ((((mem_chunk *)(chunk))->size_tag)|=(0x02))
#define SET_UMAP(chunk) ((((mem_chunk *)(chunk))->size_tag)&=(~0x02))
//經過大小算出bins 的下標。
#define SIZE_GET_INDEX(size) (size == 40? 0 :size == 48? 1:size == 56?2:3)
//按照8字節對齊
#define ALIGN_SIZE(size) (((size + ALIGN -1)/(ALIGN))*(ALIGN))
#define PREV(list) (list->prev)
#define NEXT(list) (list->next)
#define CHUNK_MEM(chunk) ((void *)(((char *)(chunk)) + 24))
#define MEM_CHUNK(chunk) ((void *)(((char *)(chunk)) - 24))
//關於鏈表的一些操做,很簡單
#define LIST_init(list){ \
(list)->next = (list)->prev = (list);\
}
#define INSERT(list,node){ \
(node)->next = (list)->next; \
(list)->next->prev = (node); \
(node)->prev = (list); \
(list)->next = (node); \
}
#define delete(list){ \
(list)->prev->next = (list)->next; \
(list)->next->prev = (list)->prev; \
}
#define for_each(list,pos) \
for((pos) = (list)->next ; \
(pos) != (list); \
(pos) = pos->next \
)
static int GLOAB_INIT = 0; //標識是否初始化
typedef struct mem_pool{ //sizeof 32B
char *mm_pool; //指向mm_pool 的尾部
struct mem_map* mm_map; //指向map 的尾部
char *tail_pool; //mem_pool 的尾部
struct mem_chunk *chunk_start;
}mem_pool;
typedef struct mem_chunk{ //sizeof 32B
int8_t size_tag;
struct mem_chunk *prev;
struct mem_chunk *next;
char *user;
}mem_chunk;
typedef struct mem_map_index{
int8_t num_chunk;
mem_chunk *first;
}mem_maps;
typedef struct mem_map{ //sizeof 16B
mem_maps list[1];
//這裏主要是爲了動態擴充數組結構的實現,原本是能夠寫[0]的,可是爲了編譯的通用性仍是寫成[1]比較好。
}mem_map;
static char *MEM_POOL; //分配內存開始的地址
static mem_pool * head; //頭控制結構
static mem_map * mem_bitmap; //bins 的位圖
static char *MEM_POOL_TAIR ; //指向內存池的尾
static char *sub_heap ; //堆內存
static char *chunk_pad ; //下面這兩個都是爲了分配內存塊的兩個變量
static char *chunk_get ;
void mem_pool_init(){
int dev_zero = open("/dev/zero",O_RDWR);
MEM_POOL = mmap((void *)0x800000000,TEMP_MEM,PROT_WRITE,MAP_PRIVATE,dev_zero,0);
//照例使用mmap從內存映射一片內存
if(!MEM_POOL){
printf("mmap is error\n");
return ;
}
head = (mem_pool *)MEM_POOL; //設置控制頭結構
head->mm_pool = MEM_POOL;
head->mm_map = ((mem_map *)((char *)MEM_POOL + 4*SIZE));
head->chunk_start = (mem_chunk *)(&(head->mm_map->list[HAFSIZE]));
chunk_pad = (char *)(head->chunk_start);
head->chunk_start = (mem_chunk *)((char *)(&(head->mm_map->list[HAFSIZE]))+ HAFSIZE*sizeof(mem_chunk));
sub_heap = (char*)(head->chunk_start);
chunk_pad = (char *)(head->chunk_start);
chunk_get = (char *)(head->chunk_start);
mem_bitmap = head->mm_map;
MEM_POOL_TAIR = (((char *)MEM_POOL) + TEMP_MEM);
GLOAB_INIT = 1; //全局標識只在這裏初始化一次
//以上就是初始化一些全局變量
}
void mem_pool_create(){
//這裏個人bins只有4個,體現原理
int index = 0;
int8_t size = 40;
char *pos = (char *)chunk_pad;
for(index = 0;index < 4;index++ ){
mem_bitmap->list[index].num_chunk = size;
mem_bitmap->list[index].first = (mem_chunk *)chunk_pad;
mem_bitmap->list[index].first->size_tag = 0;
mem_bitmap->list[index].first->user = NULL;
mem_bitmap->list[index].first->next = mem_bitmap->list[index].first->prev = mem_bitmap->list[index].first;
chunk_pad = ((char *)pos) + 32;
pos = chunk_pad;
printf("the bit %p \n",chunk_pad);
size += 8;
}
}
void * mem_malloc(size_t size){
int size_n;
if(0 >= size || MEM_POOL == NULL){
printf("init_fail or size is not real :%d \n",__LINE__);
return (void *)-1;
}
//若是數據不合法直接退出
size_n = ALIGN_SIZE(size);
//算出這個塊對齊後的大小
if(size > 64){
return (void *)mem_malloc_map(size_n);
//若是大於64調用mmap()給他映射一片內存空間
}else if(size <= 64){
if(GLOAB_INIT == 0){
mem_pool_init();
//判斷有沒有初始化
}
size_t index ;
index = SIZE_GET_INDEX(size_n);
//算出index
if(mem_bitmap->list[index].first->size_tag == 0){
return (void *)mem_malloc_heap(size_n);
//若是這個index沒有分配大小,就從堆空間先分配
}else{
mem_chunk *pos;
pos = mem_bitmap->list[index].first->next;
delete(mem_bitmap->list[index].first->next);
return (void *)((char *)pos + 3*SIZE);
}
}
}
char *mem_malloc_map(size_t size){
//使用mmap()函數從內存中映射一片空間來
//值的注意的是這裏每次都從咱們初始化堆結構的尾部來分配一片內存
char *new_map;
int dev_zero = open("/dev/zero",O_RDWR);
new_map = mmap((void *)MEM_POOL_TAIR,size+32,PROT_WRITE,MAP_PRIVATE,dev_zero,0);
((mem_chunk *)new_map)->size_tag = size+32;
SET_MAP(new_map);
SET_use(new_map);
//返回用戶區域的首地址
return (((char *)new_map) + 8);
}
char *mem_malloc_heap(size_t size){
//若是開始bin中並無空閒的空間,就從堆中分配空間進來
char *temp;
temp = chunk_get;
((mem_chunk *)chunk_get)->size_tag = size;
SET_use(chunk_get);
SET_UMAP(chunk_get);
sub_heap = (char *)(chunk_get) + 32 + size;
chunk_get = sub_heap;
return (temp + 24);
}
void mem_free(void *temp){
char *pos;
size_t size;
int index;
pos = MEM_CHUNK(temp);
size = GET_SIZE(pos);
if(size >= 65){
munmap((void *)pos,size);
}
index = SIZE_GET_INDEX(size);
SET_free(pos);
INSERT(mem_bitmap->list[index].first,((mem_chunk *)pos));
}
這裏的回收函數我處理的比較簡單,首先看看它的大小若是大於65這裏天然就是mmap()函數分配的,調用munmap()函數進行釋放,不然加進咱們的bins 以留後用,可是這裏應該由一個heap的緊縮操做判斷,我沒有寫,有興趣的朋友能夠嘗試下,我說下原理,很簡單的。markdown
這裏你們應該能看到,釋放的內存如今並無還給操做系統,而是再咱們的bins 中,緊縮操做就是找到堆的底部,向上查看,若是底層上邊的塊是空閒的而且他們的大小之和到達某一個閥值就使用munmap()函數來釋放這片內存,否則會發生「內存暴增「的問題。數據結構
版權聲明:本文爲博主原創文章,未經博主容許不得轉載。多線程