经历了两周多吧,总算把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()函数来释放这片内存,否则会发生“内存暴增“的问题。数据结构
版权声明:本文为博主原创文章,未经博主容许不得转载。多线程