打造先进的内存KV数据库-4 伙伴内存管理系统

23 篇文章 0 订阅
14 篇文章 2 订阅

伙伴算法

大概是这样的,monkey储存引擎(今年猴年了嘛~取个名字,重了再换)的内存管理使用伙伴算法,大概原理是这样的:
1.多个空闲内存块的链表,分别是不同大小的内存块,1K,2K,4K,8K,…,,按照这样的情况一直到4M,当程序申请内存时,选择一个稍大于其所要申请的块,比如申请1023字节,给1K,1024字节也给1K(为什么呢– 后文会讲),2056字节给4K。
2.如果要给的内存块没有,比如要分配4K,但是4K大小的内存块用光了,那么向上,将8K大小的分割掉,拿一个4K的分配掉,剩余的4K插入到4K的链表里面去,当一直分割,4M的最大块也没有的情况,就向操作系统申请4M的块。
3.不用的内存如何回收呢?因为给的内存块大小大于所储存的内容,所以将第一个字节用来记录内存块大小,回收的时候根据第一字节的大小扔进链表就可以啦~
PS:良好的代码结构,可以让代码重用,并减少耦合,分情况将各个功能写成短小精悍的小函数,互相调用,既增加可读性,又增加可维护性,还不容易出bug~这就是UNIX的组织思想吧~

代码实现

//storage.h
#ifndef STORAGE_H_INCLUDED
#define STORAGE_H_INCLUDED
#include "link.h"

#define SEGMENT_SIZE 1024
#define INIT_SEGMENT_NUM 32
#define MAX_FREE_PART 13    //最大2^13大小的块

typedef struct {    //伙伴系统
    LinkNode partner[MAX_FREE_PART];   //2^0~2^13=4096 空头链表
}FreeList;

FreeList freeList;
//初始时初始化INIT_SEGMENT_NUM个段,如果新插入的记录能够放进段中,则放入,每个段最多放一个,如果放不进去,则放入页中

void InitStorage(); //初始化储存引擎
void* InsertToFreeList(unsigned int size,void * pData);  //向储存引擎插入数据
void Free(void* pData); //回收内存

#endif // STORAGE_H_INCLUDED
//link.h
#ifndef LINK_H_INCLUDED
#define LINK_H_INCLUDED

typedef struct _LinkNode{
    void* pData;    //链表内容
    struct _LinkNode * next;    //下一个节点
} LinkNode;

#endif // LINK_H_INCLUDED
//storage.c
#include "storage.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void* GetFree(unsigned int);

void InsertFree(unsigned n,void *pData) //向2^n*seg区插入一段空闲空间
{
    LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));  //插入链表第一个节点
    p->pData = pData;
    p->next = freeList.partner[n].next;
    freeList.partner[n].next = p;
    return;
}

void* Splice(unsigned int  n)   //分裂节点并返回分裂出的一个节点的指向内存,另一个节点插入链表相应位置
{
    if(n > MAX_FREE_PART)   //请求大于最大支持的内存块
    {
        LinkNode* p = &freeList.partner[MAX_FREE_PART - 1]; //是否是最大的块内存不足
        if(p->next) //最大的块还有剩余~说明请求的内存太大了,不支持
        {
            fprintf(stderr,"Max support memory part is 2 ^ %d!\n",MAX_FREE_PART);
            exit(-1);
        }
        //分裂超出最大块出现在最大块已经不足的情况下,那么返回新申请的最大块给他即可
        return malloc(SEGMENT_SIZE << MAX_FREE_PART);
    }
    else
    {
        void* pBig = GetFree(n);    //索取n的空间并分裂
        void* pHalf = pBig + (SEGMENT_SIZE << (n-1));
        InsertFree(n-1,pHalf);
        return pBig;
    }
}

void* GetFree(unsigned int n)   //向空闲链表索取n*segment的空间
{
    LinkNode* p = &freeList.partner[n];
    if(p->next) //第一个节点就有空间
    {
        LinkNode *tn = p->next;
         void *t = tn->pData;
        p->next = p->next->next;    //取出并返回
        free(tn);
        return t;
    }
    else
    {
        return Splice(n+1); //要求分裂更大的块
    }
}

void* InsertToFreeList(unsigned int size,void * pData)   //使用空闲空间链表的一小段空间
{
    unsigned int n = size / SEGMENT_SIZE;
    unsigned int i = 0;
    while(n)
    {
        n >>= 1;
        i++;
    }
    void *p = GetFree(n);   //获取空间
    memset(p,i,1);  //空间第一个字节写入空间大小,为了垃圾回收
    memcpy(p+1,pData,size); //其余空间写入数据
    return p;
}

void Free(void* pData)
{
    unsigned int n = *((char*)pData);   //读出第一个字节表示的该段内存大小
    InsertFree(n,pData);    //回收内存
}

void InitStorage()
{
    //TODO:初始化一些小的内存片段供使用
}

经测试,缓存池分配10000次内存比直接调用malloc分配10000次快120倍左右
另外补充将小块内存合并的代码:

void InsertFree(unsigned n,void *pData) //向2^n*seg区插入一段空闲空间
{
    if(n > MAX_FREE_PART)
    {
        free(pData);
        return;
    }
    LinkNode* t = &freeList.partner[n];
    LinkNode* parent = t;
    t = t->next;
    while(t && t->pData < pData)            // 寻找相邻块并合并
    {
        if(t->pData + (SEGMENT_SIZE << n) == pData)
        {
            parent->next = t->next;
            //printf("combine");
            InsertFree(n+1,t->pData);   //Combine
            free(t);
            return;
        }
        parent = t;
        t = t->next;
    }
    if(t && t->pData == pData + (SEGMENT_SIZE << n))
    {
        //printf("combine");
        InsertFree(n+1,pData);  //合并并插入上个链表
    }
    else
    {
        if(!t)
        {
            LinkNode* p = malloc(sizeof(LinkNode));
            p->next = NULL;
            p->pData = pData;
           parent->next = p;
        }
        else
        {
            LinkNode* p = malloc(sizeof(LinkNode));
            p->next = t->next;
            p->pData = pData;
            t->next = p;
        }
    }
    return;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值