C语言实现简单内存分配器

为了理解虚拟内存分配规则,实现了简单的内存分配器。

该分配器基于隐式空闲链表实现,采用带边界标记的立即合并方式合并空闲块。

对于该空闲链表 ,头部包含一个字大小的占位块方便对齐,接下来包含两个字大小的头部与尾部作为链表头节点,包含一个设置大小为一个字,分配状态为已分配的块标记链表尾部。

每次分配空间首先扫描链表,寻找适配块,找到合适块则直接分配,否则申请最小一页的内存空间。

每次释放空间,将头尾分配标识位置零后,立即执行合并块操作,合并相邻空闲块空间。

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
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,我会尽力回答你的问题。首先,我们需要了解一下内存分配的基本原理。在C语言中,内存分配通常使用malloc函数。它会在堆上分配一块指定大小的空间,并返回一个指针,指向这块空间的起始地址。 我们可以使用链表来实现一个简单内存分配。具体来说,我们可以将堆划分为多个固定大小的块,然后使用链表来管理这些块。 下面是一个简单的示例代码: ``` #include <stdio.h> #include <stdlib.h> // 定义一个内存块结构体 typedef struct block { int size; // 内存块大小 struct block* next; // 下一个内存块 } block_t; // 定义一个内存池结构体 typedef struct { block_t* head; // 内存块链表头 int block_size; // 内存块大小 int num_blocks; // 内存块数量 } mem_pool_t; // 初始化内存池 void mem_pool_init(mem_pool_t* pool, int block_size, int num_blocks) { int i; block_t *p, *q; // 分配内存池空间 pool->head = (block_t*)malloc(block_size * num_blocks); pool->block_size = block_size; pool->num_blocks = num_blocks; // 初始化内存块链表 p = pool->head; for (i = 0; i < num_blocks; i++) { p->size = block_size; q = p++; q->next = p; } q->next = NULL; } // 内存分配函数 void* mem_alloc(mem_pool_t* pool, int size) { block_t *p, *q; // 遍历内存块链表,寻找第一个大小足够的内存块 for (p = pool->head, q = NULL; p != NULL; q = p, p = p->next) { if (p->size >= size) { // 将找到的内存块从链表中移除 if (q == NULL) { pool->head = p->next; } else { q->next = p->next; } // 返回内存块指针 return (void*)p; } } // 没有找到合适的内存块 return NULL; } // 内存释放函数 void mem_free(mem_pool_t* pool, void* ptr) { block_t *p, *q; // 将被释放的内存块插入到内存块链表头部 p = (block_t*)ptr; for (q = pool->head; q != NULL && q < p; q = q->next) { if (q->next > p) { break; } } p->next = q->next; q->next = p; } // 测试代码 int main() { mem_pool_t pool; int i; void* ptr[10]; // 初始化内存池 mem_pool_init(&pool, sizeof(int), 10); // 分配内存块 for (i = 0; i < 10; i++) { ptr[i] = mem_alloc(&pool, sizeof(int)); *(int*)ptr[i] = i; } // 输出内存块内容 for (i = 0; i < 10; i++) { printf("%d ", *(int*)ptr[i]); } printf("\n"); // 释放内存块 for (i = 0; i < 10; i++) { mem_free(&pool, ptr[i]); } return 0; } ``` 在这个示例代码中,我们定义了一个内存块结构体,以及一个内存池结构体。内存池结构体包含一个内存块链表头,以及内存块大小和数量等信息。我们使用mem_pool_init函数来初始化内存池,使用mem_alloc函数来分配内存块,使用mem_free函数来释放内存块。在测试代码中,我们分配了10个int类型的内存块,并存储了它们的值,然后释放了这些内存块。 需要注意的是,这个示例代码只是一个简单的演示,实际的内存分配需要考虑更多的因素,如内存对齐、内存碎片等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值