为了理解虚拟内存分配规则,实现了简单的内存分配器。
该分配器基于隐式空闲链表实现,采用带边界标记的立即合并方式合并空闲块。
对于该空闲链表 ,头部包含一个字大小的占位块方便对齐,接下来包含两个字大小的头部与尾部作为链表头节点,包含一个设置大小为一个字,分配状态为已分配的块标记链表尾部。
每次分配空间首先扫描链表,寻找适配块,找到合适块则直接分配,否则申请最小一页的内存空间。
每次释放空间,将头尾分配标识位置零后,立即执行合并块操作,合并相邻空闲块空间。
mm.h 头文件及所需宏定义
#ifndef MM_ALLOCATOR
#define MM_ALLOCATOR
/*
* 基于隐式空闲链表,使用立即合并方式
* 栈最大容量为20G
* 每个内存块均包括头部与尾部,故最小块为一个双字大小
* 双字对齐
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
// 基本常数 和 宏
#define MAX_HEAP 1024 * 1024 * 20 //栈最大容量
#define WSIZE 4 //word size
#define DSIZE 8 //double word size
#define CHUNKSIZE (1 << 12) //初始空闲块大小 与 扩展堆默认大小
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define PACK(size,alloc) ((size) | (alloc)) //包装块大小与分配状态,置于块头部或尾部
#define GET(p) (*(unsigned int *)(p)) //强转取址
#define PUT(p,val) (*(unsigned int *)(p) = (val)) //强转赋值
#define GET_SIZE(p) (GET(p) & ~0x7) //用于头部尾部返回块大小
#define GET_ALLOC(p) (GET(p) & 0x1) //返回分配状态
#define HDRP(bp) ((char *)(bp) - WSIZE) // bp为快指针指向第一位有效载荷字节 返回头部指针Header Pointer
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) //返回尾部指针 Footer Pointer
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) //指向下一个块的块指针
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) //指向前一个块的块指针
//外部接口
extern int mm_init(void); //初始化所有内存块
extern void *mm_malloc(size_t size); //分配块
extern void mm_free(void *ptr); //释放块
extern void print_mem(); //打印所有块的地址,内存大小及分配状态
extern void mem_init(void); //初始化操作堆栈
extern void *mem_sbrk(int incr); //更改堆栈大小,但不允许收缩,简化实现sbrk()
#endif
mm.c 函数具体实现
#include "mm.h"
static char *mem_heap; //堆栈栈底指针
static char *mem_brk; //堆栈栈顶指针
static char *mem_max_addr; //堆栈容量
//初始化堆栈
void mem_init(void)
{
mem_heap = (char *)malloc(MAX_HEAP);
mem_brk = (char *)mem_heap;
mem_max_addr = (char *)(mem_heap + MAX_HEAP);
}
//栈扩容
void *mem_sbrk(int incr)
{
char * old_brk = mem_brk; //保存旧指针
if(incr < 0 || (mem_brk + incr) > mem_max_addr){
errno = ENOMEM; //设置错误号
fprintf(stderr,"ERROR : mem_sbrk faild.\n");;
return (char *)-1;
}
mem_brk += incr;
return (void *)old_brk;
}
//私有函数声明
static void *extend_heap(size_t);
static void *coalesce(void *bp);
static void *find_fit(size_t asize);
static void place(void *bp,size_t asize);
//extern end
static char *heap_listp;//指向序言块的指针
void print_mem()
{
int cnt = 0;
for(char* it = heap_listp;GET_SIZE(HDRP(it)) > 0;it = NEXT_BLKP(it)){
printf("%d : addr %p size %d alloc %d\n",++cnt,it,GET_SIZE(HDRP(it)),GET_ALLOC(HDRP(it)));
}
printf("mem end\n");
//栈扩容
static void *extend_heap(size_t words)
{
char *bp;
size_t size;
size = (words & 1) ? (words + 1) * WSIZE : words * WSIZE; //双字对齐
if((long)(bp = mem_sbrk(size)) == -1) return NULL;
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(NEXT_BLKP(bp)),PACK(0,1));
return coalesce(bp);
}
//初始化创建空闲列表
int mm_init(void)
{
if((heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1) //初始块分配失败
return -1;
PUT(heap_listp, 0); //对齐填充块
PUT(heap_listp + (1*WSIZE), PACK(DSIZE,1)); //序言块头部
PUT(heap_listp + (2*WSIZE), PACK(DSIZE,1)); //序言块尾部
PUT(heap_listp + (3*WSIZE), PACK(0,1)); //栈顶指针指向结尾快
heap_listp += (2 * WSIZE); //指向序言块
if(extend_heap(CHUNKSIZE/WSIZE) == NULL) return -1; //创建初始空闲块
return 0;
}
//合并与当前块相邻的空闲块
static void *coalesce(void *bp)
{
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); //上一个块的状态
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); //下一个块的状态
size_t size = GET_SIZE(HDRP(bp)); //当前块大小
//四种情况下的合并方式
if(prev_alloc && next_alloc){
return bp;
}
else if(prev_alloc && !next_alloc){
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0));
}
else if(!prev_alloc && next_alloc){
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
bp = PREV_BLKP(bp);
}
else{
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0));
bp = PREV_BLKP(bp);
}
return bp;
}
//释放bp指向的块
void mm_free(void *bp)
{
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp),PACK(size,0));//设置头部
PUT(FTRP(bp),PACK(size,0));//设置尾部
coalesce(bp); //合并相邻块
}
void *mm_malloc(size_t size)
{
size_t asize;
size_t extendsize;
char *bp;
if(size == 0) return NULL;
if(size <= DSIZE) asize = 2 * DSIZE;
else asize = DSIZE * ((size + DSIZE + (DSIZE - 1)) / DSIZE); //用需求大小+头尾大小 向上取整
if((bp = find_fit(asize)) != NULL){
place(bp,asize);
return bp;
}
extendsize = MAX(CHUNKSIZE,asize); //请求扩展内存大小
if((bp = extend_heap(extendsize/WSIZE)) == NULL){
return NULL;
}
place(bp,asize);
return bp;
}
//首次适配法寻找合适块
static void *find_fit(size_t asize)
{
char *bp = heap_listp;
for(bp = heap_listp;GET_SIZE(HDRP(bp)) > 0;bp = NEXT_BLKP(bp)){
if(!GET_ALLOC(HDRP(bp)) && GET_SIZE(HDRP(bp)) >= asize)
return bp;
}
return NULL;
}
//分配块
static void place(void *bp, size_t asize)
{
size_t old_size = GET_SIZE(HDRP(bp));
if(old_size - asize >= DSIZE){
PUT(HDRP(bp),PACK(asize,1));
PUT(FTRP(bp),PACK(asize,1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp),PACK(old_size - asize, 0));
PUT(FTRP(bp),PACK(old_size - asize, 0));
}
else{ZZ
PUT(HDRP(bp),PACK(old_size,1));
PUT(FTRP(bp),PACK(old_size,1));
}
}
main测试函数
#include "mm.h"
int main()
{
mem_init();
mm_init();
char *p = (char *)mm_malloc((sizeof(char)) * 10);
print_mem();
if(p == NULL){
fprintf(stderr,"mm_malloc error!\n");
exit(1);
}
printf("addr of p:%p\n",p);
mm_free(p);
print_mem();
printf("free ok\n");
char *p1 = (char *)mm_malloc((sizeof(char)) * 20);
print_mem();
printf("addr of p1 :%p\n",p1);
char *p2 = (char *)mm_malloc(sizeof(char) * 5000);
print_mem();
printf("addr of p2 :%p\n",p2);
mm_free(p1);
print_mem();
mm_free(p2);
print_mem();
return 0;
}
运行结果
1 : addr 0x7f6115d44018 size 8 alloc 1
2 : addr 0x7f6115d44020 size 24 alloc 1
3 : addr 0x7f6115d44038 size 4072 alloc 0
mem end
addr of p:0x7f6115d44020
1 : addr 0x7f6115d44018 size 8 alloc 1
2 : addr 0x7f6115d44020 size 4096 alloc 0
mem end
free ok
1 : addr 0x7f6115d44018 size 8 alloc 1
2 : addr 0x7f6115d44020 size 32 alloc 1
3 : addr 0x7f6115d44040 size 4064 alloc 0
mem end
addr of p1 :0x7f6115d44020
1 : addr 0x7f6115d44018 size 8 alloc 1
2 : addr 0x7f6115d44020 size 32 alloc 1
3 : addr 0x7f6115d44040 size 5008 alloc 1
4 : addr 0x7f6115d453d0 size 4064 alloc 0
mem end
addr of p2 :0x7f6115d44040
1 : addr 0x7f6115d44018 size 8 alloc 1
2 : addr 0x7f6115d44020 size 32 alloc 0
3 : addr 0x7f6115d44040 size 5008 alloc 1
4 : addr 0x7f6115d453d0 size 4064 alloc 0
mem end
1 : addr 0x7f6115d44018 size 8 alloc 1
2 : addr 0x7f6115d44020 size 9104 alloc 0
mem end