C语言——内存池的设计和实现

看完了之前两套代码后,我打算自己动手实现一个简单的内存池,我的思路与代码二类似,这样做的好处是可以给不同的对象使用不同的内存池,可以相互隔绝:memorypool下面挂一个memory的链表,可以选择动态扩展长度,每次分配和回收都以memory为单位;memorypool中的free_list 采用单链表实现,used_list采用双链表实现,因为考虑到,分配给某个对象实例一个内存空间后,方便查看该对象还有哪些实例,所以used_list采用了双链表。

1.数据的组织结构:

{
    int mempool_size;
    int memory_size;//block_size + sizeof(manager information) = mem_size
    int block_size;
    int total_memory_cnt;
    int used_memory_cnt;
    int auto_extend;
    struct Memory* free_list;
    struct Memory* used_list;
}memorypool;

{
    int memory_size;
    int block_size;
    struct Memory* prev;
    struct Memory* next;
    struct Memorypool* mp;
    char* block;
}memory

memorypool是memory的信息管理;memory是向系统申请的内存块,包括信息管理部分和实际分配给用户使用的部分;

2.该内存池对外提供的API:

memorypool* memorypool_create(int block_request_size, int memory_init_quantity, bool memory_extend);
char* memory_malloc(memorypool* mp,int data_size);
int memorypool_info_get(memorypool* mp);
int memory_free(char** p_bk);
int memorypool_destroy(memorypool* mp);

3.源码:

头文件:memorypool.h

#ifndef _MEMORYPOOL_H_
#define _MEMORYPOOL_H_
#include 
#include 
#include 
typedef struct Memorypool
{
    int mempool_size;
    int memory_size;//block_size + sizeof(manager information) = mem_size
    int block_size;
    int total_memory_cnt;
    int used_memory_cnt;
    int auto_extend;
    struct Memory* free_list;
    struct Memory* used_list;
}memorypool;

typedef struct Memory 
{
    int memory_size;
    int block_size;
    struct Memory* prev;
    struct Memory* next;
    struct Memorypool* mp;
    char* block;
}memory;
memorypool* memorypool_create(int block_request_size, int memory_init_quantity, bool memory_extend);
char* memory_malloc(memorypool* mp,int data_size);
int memorypool_info_get(memorypool* mp);
int memory_free(char** p_bk);
int memorypool_destroy(memorypool* mp);
memory* memory_creat(int memory_size, memorypool* mp);

#endif




源文件:memorypool.c

#include "memorypool.h"

memorypool* memorypool_create(int block_request_size, int memory_init_quantity, bool memory_extend)
{
    memorypool* mp = NULL;
    mp = (memorypool*)malloc(sizeof(memorypool));
    memset(mp, 0, sizeof(memorypool));
    mp->block_size = block_request_size;
    mp->memory_size = block_request_size + sizeof(memory); 
    mp->auto_extend = memory_extend;

    for (int i = memory_init_quantity; i > 0; i--)
    {
        memory* mm = NULL;
        mm = memory_creat(mp->memory_size , mp);
    }
    return mp;
};

char* memory_malloc(memorypool* mp, int data_size)
{
    memory* p_memory = NULL;
    if (mp->block_size < data_size)
    {
        return NULL;
    }
    if (NULL == mp->free_list)
    {
        if (mp->auto_extend == 1)
            memory_creat(mp->memory_size, mp);
        else
            return NULL;
    }
    p_memory = mp->free_list;
    mp->free_list = mp->free_list->next;
    p_memory->next = NULL;
    if (mp->used_list)
    {
        mp->used_list->prev->next = p_memory;
        p_memory->prev = mp->used_list->prev;
    }
    else
    {
        mp->used_list = p_memory;
    }
    p_memory->next = mp->used_list;
    mp->used_list->prev = p_memory;
    mp->used_list = p_memory;
    mp->used_memory_cnt += 1;
    return p_memory->block;
};
int memorypool_info_get(memorypool* mp) 
{
    printf("current mempool_size:%d\n\
current memory_size:%d\n\
current block_size:%d\n\
total_memory_cnt:%d\n\
used_memory_cnt:%d\n",mp->mempool_size,mp->memory_size,mp->block_size, \
mp->total_memory_cnt,mp->used_memory_cnt);
    return 0;
};
int memory_free(char** p_bk)
{
    memory* p_memory = (memory* )(*p_bk + sizeof(memory));//错误
    memory* p_memory = (memory* )(*p_bk - sizeof(memory)/(sizeof(char)));//正确
    p_memory->next->prev = p_memory->prev;
    p_memory->prev->next = p_memory->next;
    p_memory->next = NULL;
    p_memory->prev = NULL;
    if (p_memory->mp->used_memory_cnt == 1)
        p_memory->mp->used_list = NULL;
    p_memory->next = p_memory->mp->free_list;
    p_memory->mp->free_list = p_memory;
    p_memory->mp->used_memory_cnt -= 1;
    *p_bk = NULL;
    return 0;
};

int memorypool_destroy(memorypool* mp) 
{
    memory* mm = NULL;
    if (mp->free_list)
    {
        mm = mp->free_list;
        mp->free_list = mp->free_list->next;
        free(mm);
        mm = NULL;
    }
    if (mp->used_list)
    {
        mp->used_list->prev->next = NULL;
        mm = mp->used_list;
        mp->used_list = mp->used_list->next;
        free(mm);
        mm = NULL;
    }
    free(mp);

    return 0;

}

memory* memory_creat(int memory_size, memorypool* mp)
{
    memory* mm = NULL;
    char* bk = NULL;
    char* memory_start = (char*)malloc(memory_size);
    memset(memory_start, 0, memory_size);
    mm = (memory*)memory_start;
    memset(mm, 0, sizeof(memory));
    bk = memory_start - sizeof(memory);//错误
    bk =memory_start +sizeof(memory)/(sizeof(char));//正确
    mm->memory_size = memory_size;
    mm->block_size = memory_size - sizeof(memory);
    mm->mp = mp;
    mm->block = bk;
    mm->next = mp->free_list;
    mp->free_list = mm;
    mp->total_memory_cnt += 1;
    mp->mempool_size += mp->memory_size;

    return mm;

};



测试文件:main.c

#include "memorypool.h"
#include "time.h"

int main() 
{
    memorypool* mp = NULL;
    int memory_init_quantity = 5;
    bool memory_extend = 1;
    int block_request_size =1024;
    time_t start = 0;
    time_t end = 0;
    int count = 0;
    mp = memorypool_create(block_request_size, memory_init_quantity, memory_extend);
    struct test 
    {
        int a;
    };
    struct test* p_data;
    start = time(NULL);
    while (1)
    {
        p_data = (struct test*)memory_malloc(mp, sizeof(struct test));
        //p_data = (struct test*)malloc(sizeof(struct test));
        p_data->a = 100;
        memory_free((char**)&p_data);
        //free(p_data);
        end = time(NULL);
        count += 1;
        if (end == (start + 300))
        {
            printf("%d", count);
            break;
        }
    }
    /*

    p_data = (struct test*)memory_malloc(mp, sizeof(struct test));
    memory_free((char**)&p_data[0]);
    memorypool_info_get(mp);
    memorypool_destroy(mp);
    */

    system("pause");
    return 0; 
}

4.遇到的问题

一开始,我在写int memory_free(char* bk)函数时,纠结于p_data的类型是struct test*,与形参类型char*不一致,使用了笨办法,强制转换指针类型;

之后,考虑到memory_free()之后,被free掉的指针,仍然可以访问,我索性把要free的指针的地址传入了函数,在memory_free()函数内将该指针置为了NULL;

最后,在对比memory_malloc\free与系统函数malloc\free的性能时,突然想起看看系统函数是怎么处理这个问题的,发现事情其实是这样的:系统函数malloc返回的是void*(无类型,即所有类型),我们在使用该地址时需要强制转换为我们需要的指针类型;而free函数的形参是void*,我们传进去的实参,无论是何种类型的指针,均可以隐式的转换为void*,使用了系统函数free()之后,free的指针也依然存在且可以访问,为了避免出现野指针,我们需要手动将其置为NULL;但指针的指针没有这样的用法。

5.收获

对指针的类型转换有了进一步的理解:void*需要强制转换为其他类型;而其他类型的指针可以隐式转换为void*;指针的指针只能强制转换,没有这种隐式转换。

相同时间内反复malloc和free,内存池的效率大概是系统函数的3倍;

2019/2/17:

在两个线程分别向内存池申请内存块、释放内存块到内存池时,出现了mp指针的指向错误,

经解决,是内存池在memory_creat和memory_free时,对p_memory->block指针赋值错误造成的。

该bug将在上面代码中标记出来。

犯这样的错误是因为没有搞清楚:1.地址+size,想得到地址,因注意size的进制、量度;

2.堆是从低地址开始分配的,栈是从高地址开始分配的。没有区分地址分配的方向。见下图。

C语言——内存池的设计和实现_第1张图片

你可能感兴趣的:(linux,c)