内存池的实现

概述:本文介绍用户层内存池的实现

Q:为什么需要内存池?

A:在项目中,用户层通过malloc系统调用申请内存的次数可能很多 ,每一次malloc系统调用 ,都会引起用户态------内核态的切换 ,这样的开销对性能的影响是不容忽视的,尤其是当你为了申请一段很小的空间而调用malloc时,性能的损失达到了最大化

Q:什么是内存池?
解决方案:只调用一次malloc

A:只调用一次malloc申请足够大的内存空间,后续需要使用内存时,从已申请的内存中取出一块,这就是内存池的概念

这样就避免了频繁进行malloc系统调用,大大降低了性能损失

内存池的实现

思路:

1.内存池的空间由一次malloc调用分配

2.此时的内存池是一整块连续的内存空间

3.使用时,每次应该分配出一小块(固定大小或任意大小)

4.用户调用malloc申请内存,应该得到一块空间的起始地址

5.用户free归还一块内存时,这块内存需要能被重复使用

为了简要说明原理,本文介绍分配固定大小的内存池版本

后面使用到的二级指针,不懂的朋友可以先移步博主的 数据结构专栏------二级指针 这篇文章

1.内存池结构体定义:

c 复制代码
typedef struct mempool_s {
    int block_size; // 内存池最小单元大小
    int free_count; // 空余block数量

    char *free_ptr; // 当前空余的块的地址 
    char *mem; // 整个内存池的首地址
} mempool_t;

2.初始化,为内存池申请空间:

c 复制代码
int mp_init(mempool_t *m, int size) { // 初始化内存池,指定大小

    if (!m) return -1;
    if (size < 16) size = 16; // 最小内存池尺寸为 16字节

    m->block_size = size;

    m->mem = (char *)malloc(MEM_PAGE_SIZE); // 为内存池分配空间
    if (!m->mem) return -1;
    m->free_ptr = m->mem;   // 当前空闲的区域指针
    m->free_count = MEM_PAGE_SIZE / size;
    
    // 二级指针
    // 将一整块内存抽象为多块:块大小为size,每一块的首地址的前8个字节保存下一块的首地址
    int i = 0;
    char *ptr = m->free_ptr;
    for (i = 0;i < m->free_count; i++) {
        *(char **)ptr = ptr + size; // 将当前块的首地址的前8个字节保存下一块的首地址
        ptr += size;
    }
    *(char **)ptr = NULL; // 最后一块

    return 0;
}

3.用户层接口:malloc

c 复制代码
void *mp_alloc(mempool_t *m) { // 向内存池申请一块内存
    
    if (!m || m->free_count == 0) return NULL; // 如果内存池不存在或没有剩余空间

    void *ptr = m->free_ptr;

    m->free_ptr = *(char **)ptr; // 更新free指针,指向下一个空余的内存块
    m->free_count --;

    return ptr;
}

4.用户层接口:free

c 复制代码
void mp_free(mempool_t *m, void *ptr) {

    *(char **)ptr = m->free_ptr; // 将即将被回收的块,指向当前空余的块
    m->free_ptr = (char *)ptr; // 将当前空余的块修改为即将回收的块
    // 相当于把回收的块插入空余队列的第一个位置,优先分配回收的块
    m->free_count ++;
}

5.测试函数main

c 复制代码
int  main() { // 测试

    mempool_t m;

    mp_init(&m, 32);

	void *p1 = mp_alloc(&m);
	printf("1: mp_alloc: %p\n", p1);

	void *p2 = mp_alloc(&m);
	printf("2: mp_alloc: %p\n", p2);

	void *p3 = mp_alloc(&m);
	printf("3: mp_alloc: %p\n", p3);

	void *p4 = mp_alloc(&m);
	printf("4: mp_alloc: %p\n", p4);

	mp_free(&m, p2);

	void *p5 = mp_alloc(&m);
	printf("5: mp_alloc: %p\n", p5);

    return 0;
}

推荐学习 https://xxetb.xetslk.com/s/p5Ibb

相关推荐
小小小小钰儿20 小时前
2.1-Windows本地部署大模型
人工智能·windows·计算机·网络安全·操作系统·编程
流浪0011 天前
Linux系统篇39——线程(四) pthread库的由来和线程的创建与等待
linux·操作系统·线程·pthread库
流浪0012 天前
Linux系统篇42——线程(七) pthread库管理线程的工作流,从创建到回收
linux·操作系统·线程·线程创建到销毁
云计算练习生2 天前
什么是 Linux?从 Unix 到开源生态的完整脉络
linux·开源·操作系统·unix
云计算练习生3 天前
什么是上下文切换?进程切换时 CPU 到底在保存什么
linux·服务器·操作系统·进程切换
CS创新实验室5 天前
为什么三台机器就能选出一个“带头大哥“?——Raft 一致性算法,一次讲透
分布式·操作系统
sweet丶5 天前
iOS 为什么没有内存交换分区(swap)
操作系统
我爱cope11 天前
【操作系统 | 计算机硬件:CPU、内存与 I/O 如何支撑操作系统?】
学习·操作系统
NineData11 天前
NineData 与统信服务器操作系统完成兼容认证
数据库·oracle·操作系统·软件·数据库管理工具·ninedata·统信
DataScience_201912 天前
系统|dup2:文件描述符复制的艺术
操作系统