您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

带你用纯C实现一个内存池(图文结合)

时间:2022-07-29 11:45:05  来源:  作者:linux技术栈

前言

本文从零到一,手把手实现一个内存池。

比较出名的内存池有jemalloc和tcmalloc,这两个都是全局内存池,比较推荐使用tcmalloc。

本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章

为什么要用内存池

为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者继承的屎山中,非常容易出现内存泄漏导致mmo的问题。

为了解决这两个问题,内存池就应运而生了。内存池预先分配一大块内存来做一个内存池,业务中的内存分配和释放都由这个内存池来管理,内存池内的内存不足时其内部会自己申请。所以内存碎片的问题就交由内存池的算法来优化,而内存泄漏的问题只需要遵守内存池提供的api,就非常容易避免内存泄漏了。

即使出现了内存泄漏,排查的思路也很清晰。1.检查是不是内存池的问题;2.如果不是内存池的问题,就检查是不是第三方库的内存泄漏。

内存池的使用场景

  1. 全局内存池
  2. 一个连接一个内存池(本文实现这个场景的内存池)

设计一个内存池

总体介绍

由于本文是一个连接一个内存池,所以后续介绍和代码都是以4k为分界线,大于4k的我们认为是大块内存;小于4k的我们认为是小块内存。并且注意这里的4k,并不是严格遵照4096,而是在描述上,用4k比较好描述。

在真正使用内存之前,内存池提前分配一定数量且大小相等的内存块以作备用,当真正被用户调用api分配内存的时候,直接从内存块中获取内存(指小块内存),当内存块不够用了,再有内存池取申请新的内存块。而如果是需要大块内存,则内存池直接申请大块内存再返回给用户。

内存池:就是将这些提前申请的内存块组织管理起来的数据结构,内存池实现原理主要分为分配,回收,扩容三部分。

内存池原理之小块内存:分配=> 内存池预申请一块4k的内存块,这里称为block,即block=4k内存块。当用户向内存池申请内存size小于4k时,内存池从block的空间中划分出去size空间,当再有新申请时,再划分出去。扩容=> 直到block中的剩余空间不足以分配size大小,那么此时内存池会再次申请一块block,再从新的block中划分size空间给用户。回收=> 每一次申请小内存,都会在对应的block中引用计数加1,每一次释放小内存时,都会在block中引用计数减1,只有当引用计数为零的时候,才会回收block使他重新成为空闲空间,以便重复利用空间。这样,内存池避免频繁向内核申请/释放内存,从而提高系统性能。

内存池原理之大块内存:分配=> 因为大块内存是大于4k的,所以内存池不预先申请内存,也就是用户申请的时候,内存池再申请内存,然后返回给用户。扩容=> 大块内存不存在扩容。回收=> 对于大块内存来说,回收就直接free掉即可。

上面理论讲完了,下面来介绍如何管理小块内存和大块内存。

 

小块内存的分配与管理

在创建内存池的时候,会预先申请一块4k的内存,并且在起始处将pool的结构体和node的结构体放进去,

在创建内存池的时候,会预先申请一块4k的内存,并且在起始处将pool的结构体和node的结构体放进去,从last开始一直到end都是空闲内存,<last , end >中间的区域就用来存储小块内存。每一次mp_malloc,就将last指针后移,直到 e n d − l a s t < s i z e end - last < sizeend−last<size 时,进行扩容,将新block的last后移即可。

初始状态

分配内存

扩容

大块内存的分配与管理

对于大块内存,前面已经说了,用户申请的时候,内存池才申请

  • 申请一块大内存

再申请一块大内存

内存池代码实现

向外提供的api

  • mp_create_pool:创建一个线程池,其核心是创建struct mp_pool_s这个结构体,并申请4k内存,将各个指针指向上文初始状态的图一样。
  • mp_destroy_pool:销毁内存池,遍历小块结构体和大块结构体,进行free释放内存
  • mp_malloc:提供给用户申请内存的api
  • mp_calloc:通过mp_malloc申请内存后置零,相当于calloc
  • mp_free:释放由mp_malloc返回的内存
  • mp_reset_pool:将block的last置为初始状态,销毁所有大块内存
  • monitor_mp_poll:监控内存池状态
struct mp_pool_s *mp_create_pool(size_t size);

void mp_destroy_pool(struct mp_pool_s *pool);

void *mp_malloc(struct mp_pool_s *pool, size_t size);

void *mp_calloc(struct mp_pool_s *pool, size_t size);

void mp_free(struct mp_pool_s *pool, void *p);

void mp_reset_pool(struct mp_pool_s *pool);

void monitor_mp_poll(struct mp_pool_s *pool, char *tk);

相关结构体的定义

mp_pool_s 就是整个内存池的管理结构,我们做的内存池是一个连接一个内存池,所以对于整个程序而言,内存池对象是有很多个的。

可能读者会有疑问,有了head,为什么还有current,是因为如果一个block剩余空间小于size超过一定次数后,将current指向下一个block,这样就加快内存分配效率,减少遍历次数。

//每4k一block结点
struct mp_node_s {
    unsigned char *end;//块的结尾
    unsigned char *last;//使用到哪了
    struct mp_node_s *next;//链表
    int quote;//引用计数
    int fAIled;//失效次数
};

struct mp_large_s {
    struct mp_large_s *next;//链表
    int size;//alloc的大小
    void *alloc;//大块内存的起始地址
};

struct mp_pool_s {
    struct mp_large_s *large;
    struct mp_node_s *head;
    struct mp_node_s *current;
};

内存对齐

访问速度是内存对齐的原因之一,另外一个原因是某些平台(arm)不支持未内存对齐的访问

在4k里面划分内存,那么必然有很多地方是不对齐的,所以这里提供两个内存对齐的函数。那么为什么要内存对齐呢?其一:提高访问速度;其二:某些平台arm不支持未对其的内存访问,会出错。

#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))

创建与销毁内存池

创建一个线程池,其核心是创建struct mp_pool_s这个结构体,并申请4k内存,将各个指针指向上文初始状态的图一样。
销毁内存池,遍历小块结构体和大块结构体,进行free释放内存。

//创建内存池
struct mp_pool_s *mp_create_pool(size_t size) {
    struct mp_pool_s *pool;
    if (size < PAGE_SIZE || size % PAGE_SIZE != 0) {
        size = PAGE_SIZE;
    }
    //分配4k以上不用malloc,用posix_memalign
    /*
        int posix_memalign (void **memptr, size_t alignment, size_t size);
     */

    int ret = posix_memalign((void **) &pool, MP_ALIGNMENT, size); //4K + mp_pool_s
    if (ret) {
        return NULL;
    }
    pool->large = NULL;
    pool->current = pool->head = (unsigned char *) pool + sizeof(struct mp_pool_s);
    pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
    pool->head->end = (unsigned char *) pool + PAGE_SIZE;
    pool->head->failed = 0;

    return pool;
}

//销毁内存池
void mp_destroy_pool(struct mp_pool_s *pool) {
    struct mp_large_s *large;
    for (large = pool->large; large; large = large->next) {
        if (large->alloc) {
            free(large->alloc);
        }
    }

    struct mp_node_s *cur, *next;
    cur = pool->head->next;

    while (cur) {
        next = cur->next;
        free(cur);
        cur = next;
    }
    free(pool);
}

提供给用户的内存申请api

申请的内存以size做区分,如果大于4k就分配大块内存,小于4k就去block里面划分。

 

//分配内存
void%20*mp_malloc(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20if%20(size%20<=%200)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20if%20(size%20>%20PAGE_SIZE%20-%20sizeof(struct%20mp_node_s))%20{
%20%20%20%20%20%20%20%20//large
%20%20%20%20%20%20%20%20return%20mp_malloc_large(pool,%20size);
%20%20%20%20}
%20%20%20%20else%20{
%20%20%20%20%20%20%20%20//small
%20%20%20%20%20%20%20%20unsigned%20char%20*mem_addr%20=%20NULL;
%20%20%20%20%20%20%20%20struct%20mp_node_s%20*cur%20=%20NULL;
%20%20%20%20%20%20%20%20cur%20=%20pool->current;
%20%20%20%20%20%20%20%20while%20(cur)%20{
%20%20%20%20%20%20%20%20%20%20%20%20mem_addr%20=%20mp_align_ptr(cur->last,%20MP_ALIGNMENT);
%20%20%20%20%20%20%20%20%20%20%20%20if%20(cur->end%20-%20mem_addr%20>=%20size)%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->quote++;//引用+1
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->last%20=%20mem_addr%20+%20size;
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20mem_addr;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20else%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20=%20cur->next;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20return%20mp_malloc_block(pool,%20size);//%20open%20new%20space
%20%20%20%20}
}
void%20*mp_calloc(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20void%20*mem_addr%20=%20mp_malloc(pool,%20size);
%20%20%20%20if%20(mem_addr)%20{
%20%20%20%20%20%20%20%20memset(mem_addr,%200,%20size);
%20%20%20%20}
%20%20%20%20return%20mem_addr;
}
小块内存block扩容

 

所有的block都%20e%20n%20d%20−%20l%20a%20s%20t%20<%20s%20i%20z%20e%20end%20-%20last%20<%20sizeend−last<size%20时,进行扩容,将新block的last后移即可。

 

//new%20block%204k
void%20*mp_malloc_block(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20unsigned%20char%20*block;
%20%20%20%20int%20ret%20=%20posix_memalign((void%20**)%20&block,%20MP_ALIGNMENT,%20PAGE_SIZE);%20//4K
%20%20%20%20if%20(ret)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20struct%20mp_node_s%20*new_node%20=%20(struct%20mp_node_s%20*)%20block;
%20%20%20%20new_node->end%20=%20block%20+%20PAGE_SIZE;
%20%20%20%20new_node->next%20=%20NULL;

%20%20%20%20unsigned%20char%20*ret_addr%20=%20mp_align_ptr(block%20+%20sizeof(struct%20mp_node_s),%20MP_ALIGNMENT);

%20%20%20%20new_node->last%20=%20ret_addr%20+%20size;
%20%20%20%20new_node->quote++;

%20%20%20%20struct%20mp_node_s%20*current%20=%20pool->current;
%20%20%20%20struct%20mp_node_s%20*cur%20=%20NULL;

%20%20%20%20for%20(cur%20=%20current;%20cur->next;%20cur%20=%20cur->next)%20{
%20%20%20%20%20%20%20%20if%20(cur->failed++%20>%204)%20{
%20%20%20%20%20%20%20%20%20%20%20%20current%20=%20cur->next;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20//now%20cur%20=%20last%20node
%20%20%20%20cur->next%20=%20new_node;
%20%20%20%20pool->current%20=%20current;
%20%20%20%20return%20ret_addr;
}
分配大块内存

 

//size>4k
void%20*mp_malloc_large(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20unsigned%20char%20*big_addr;
%20%20%20%20int%20ret%20=%20posix_memalign((void%20**)%20&big_addr,%20MP_ALIGNMENT,%20size);%20//size
%20%20%20%20if%20(ret)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}

%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20//released%20struct%20large%20resume
%20%20%20%20int%20n%20=%200;
%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{
%20%20%20%20%20%20%20%20if%20(large->alloc%20==%20NULL)%20{
%20%20%20%20%20%20%20%20%20%20%20%20large->size%20=%20size;
%20%20%20%20%20%20%20%20%20%20%20%20large->alloc%20=%20big_addr;
%20%20%20%20%20%20%20%20%20%20%20%20return%20big_addr;
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20if%20(n++%20>%203)%20{
%20%20%20%20%20%20%20%20%20%20%20%20break;//%20为了避免过多的遍历,限制次数
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20large%20=%20mp_malloc(pool,%20sizeof(struct%20mp_large_s));
%20%20%20%20if%20(large%20==%20NULL)%20{
%20%20%20%20%20%20%20%20free(big_addr);
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20large->size%20=%20size;
%20%20%20%20large->alloc%20=%20big_addr;
%20%20%20%20large->next%20=%20pool->large;
%20%20%20%20pool->large%20=%20large;
%20%20%20%20return%20big_addr;
}

释放内存

如果是大块内存,找到之后直接释放;如果是小块内存,将引用计数减1,如果引用计数为0则重置last。

 

//释放内存
void%20mp_free(struct%20mp_pool_s%20*pool,%20void%20*p)%20{
%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{//大块
%20%20%20%20%20%20%20%20if%20(p%20==%20large->alloc)%20{
%20%20%20%20%20%20%20%20%20%20%20%20free(large->alloc);
%20%20%20%20%20%20%20%20%20%20%20%20large->size%20=%200;
%20%20%20%20%20%20%20%20%20%20%20%20large->alloc%20=%20NULL;
%20%20%20%20%20%20%20%20%20%20%20%20return;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20//小块%20引用-1
%20%20%20%20struct%20mp_node_s%20*cur%20=%20NULL;
%20%20%20%20for%20(cur%20=%20pool->head;%20cur;%20cur%20=%20cur->next)%20{
//%20%20%20%20%20%20%20%20printf("cur:%p%20%20%20p:%p%20%20%20end:%pn",%20(unsigned%20char%20*)%20cur,%20(unsigned%20char%20*)%20p,%20(unsigned%20char%20*)%20cur->end);
%20%20%20%20%20%20%20%20if%20((unsigned%20char%20*)%20cur%20<=%20(unsigned%20char%20*)%20p%20&&%20(unsigned%20char%20*)%20p%20<=%20(unsigned%20char%20*)%20cur->end)%20{
%20%20%20%20%20%20%20%20%20%20%20%20cur->quote--;
%20%20%20%20%20%20%20%20%20%20%20%20if%20(cur->quote%20==%200)%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(cur%20==%20pool->head)%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pool->head->last%20=%20(unsigned%20char%20*)%20pool%20+%20sizeof(struct%20mp_pool_s)%20+%20sizeof(struct%20mp_node_s);
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->last%20=%20(unsigned%20char%20*)%20cur%20+%20sizeof(struct%20mp_node_s);
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->failed%20=%200;
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pool->current%20=%20pool->head;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20return;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
}
内存池测试

 

//
//%20Created%20by%2068725%20on%202022/7/26.
//
#include%20<stdlib.h>
#include%20<stdio.h>
#include%20<string.h>

#define%20PAGE_SIZE%204096
#define%20MP_ALIGNMENT%2016
#define%20mp_align(n,%20alignment)%20(((n)+(alignment-1))%20&%20~(alignment-1))
#define%20mp_align_ptr(p,%20alignment)%20(void%20*)((((size_t)p)+(alignment-1))%20&%20~(alignment-1))

//每4k一block结点
struct%20mp_node_s%20{
%20%20%20%20unsigned%20char%20*end;//块的结尾
%20%20%20%20unsigned%20char%20*last;//使用到哪了
%20%20%20%20struct%20mp_node_s%20*next;//链表
%20%20%20%20int%20quote;//引用计数
%20%20%20%20int%20failed;//失效次数
};

struct%20mp_large_s%20{
%20%20%20%20struct%20mp_large_s%20*next;//链表
%20%20%20%20int%20size;//alloc的大小
%20%20%20%20void%20*alloc;//大块内存的起始地址
};

struct%20mp_pool_s%20{
%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20struct%20mp_node_s%20*head;
%20%20%20%20struct%20mp_node_s%20*current;
};

struct%20mp_pool_s%20*mp_create_pool(size_t%20size);

void%20mp_destroy_pool(struct%20mp_pool_s%20*pool);

void%20*mp_malloc(struct%20mp_pool_s%20*pool,%20size_t%20size);

void%20*mp_calloc(struct%20mp_pool_s%20*pool,%20size_t%20size);

void%20mp_free(struct%20mp_pool_s%20*pool,%20void%20*p);

void%20mp_reset_pool(struct%20mp_pool_s%20*pool);

void%20monitor_mp_poll(struct%20mp_pool_s%20*pool,%20char%20*tk);


void%20mp_reset_pool(struct%20mp_pool_s%20*pool)%20{
%20%20%20%20struct%20mp_node_s%20*cur;
%20%20%20%20struct%20mp_large_s%20*large;

%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{
%20%20%20%20%20%20%20%20if%20(large->alloc)%20{
%20%20%20%20%20%20%20%20%20%20%20%20free(large->alloc);
%20%20%20%20%20%20%20%20}
%20%20%20%20}

%20%20%20%20pool->large%20=%20NULL;
%20%20%20%20pool->current%20=%20pool->head;
%20%20%20%20for%20(cur%20=%20pool->head;%20cur;%20cur%20=%20cur->next)%20{
%20%20%20%20%20%20%20%20cur->last%20=%20(unsigned%20char%20*)%20cur%20+%20sizeof(struct%20mp_node_s);
%20%20%20%20%20%20%20%20cur->failed%20=%200;
%20%20%20%20%20%20%20%20cur->quote%20=%200;
%20%20%20%20}
}

//创建内存池
struct%20mp_pool_s%20*mp_create_pool(size_t%20size)%20{
%20%20%20%20struct%20mp_pool_s%20*pool;
%20%20%20%20if%20(size%20<%20PAGE_SIZE%20||%20size%20%%20PAGE_SIZE%20!=%200)%20{
%20%20%20%20%20%20%20%20size%20=%20PAGE_SIZE;
%20%20%20%20}
%20%20%20%20//分配4k以上不用malloc,用posix_memalign
%20%20%20%20/*
%20%20%20%20%20%20%20%20int%20posix_memalign%20(void%20**memptr,%20size_t%20alignment,%20size_t%20size);
%20%20%20%20%20*/

%20%20%20%20int%20ret%20=%20posix_memalign((void%20**)%20&pool,%20MP_ALIGNMENT,%20size);%20//4K%20+%20mp_pool_s
%20%20%20%20if%20(ret)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20pool->large%20=%20NULL;
%20%20%20%20pool->current%20=%20pool->head%20=%20(unsigned%20char%20*)%20pool%20+%20sizeof(struct%20mp_pool_s);
%20%20%20%20pool->head->last%20=%20(unsigned%20char%20*)%20pool%20+%20sizeof(struct%20mp_pool_s)%20+%20sizeof(struct%20mp_node_s);
%20%20%20%20pool->head->end%20=%20(unsigned%20char%20*)%20pool%20+%20PAGE_SIZE;
%20%20%20%20pool->head->failed%20=%200;

%20%20%20%20return%20pool;
}

//销毁内存池
void%20mp_destroy_pool(struct%20mp_pool_s%20*pool)%20{
%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{
%20%20%20%20%20%20%20%20if%20(large->alloc)%20{
%20%20%20%20%20%20%20%20%20%20%20%20free(large->alloc);
%20%20%20%20%20%20%20%20}
%20%20%20%20}

%20%20%20%20struct%20mp_node_s%20*cur,%20*next;
%20%20%20%20cur%20=%20pool->head->next;

%20%20%20%20while%20(cur)%20{
%20%20%20%20%20%20%20%20next%20=%20cur->next;
%20%20%20%20%20%20%20%20free(cur);
%20%20%20%20%20%20%20%20cur%20=%20next;
%20%20%20%20}
%20%20%20%20free(pool);
}

//size>4k
void%20*mp_malloc_large(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20unsigned%20char%20*big_addr;
%20%20%20%20int%20ret%20=%20posix_memalign((void%20**)%20&big_addr,%20MP_ALIGNMENT,%20size);%20//size
%20%20%20%20if%20(ret)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}

%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20//released%20struct%20large%20resume
%20%20%20%20int%20n%20=%200;
%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{
%20%20%20%20%20%20%20%20if%20(large->alloc%20==%20NULL)%20{
%20%20%20%20%20%20%20%20%20%20%20%20large->size%20=%20size;
%20%20%20%20%20%20%20%20%20%20%20%20large->alloc%20=%20big_addr;
%20%20%20%20%20%20%20%20%20%20%20%20return%20big_addr;
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20if%20(n++%20>%203)%20{
%20%20%20%20%20%20%20%20%20%20%20%20break;//%20为了避免过多的遍历,限制次数
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20large%20=%20mp_malloc(pool,%20sizeof(struct%20mp_large_s));
%20%20%20%20if%20(large%20==%20NULL)%20{
%20%20%20%20%20%20%20%20free(big_addr);
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20large->size%20=%20size;
%20%20%20%20large->alloc%20=%20big_addr;
%20%20%20%20large->next%20=%20pool->large;
%20%20%20%20pool->large%20=%20large;
%20%20%20%20return%20big_addr;
}

//new%20block%204k
void%20*mp_malloc_block(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20unsigned%20char%20*block;
%20%20%20%20int%20ret%20=%20posix_memalign((void%20**)%20&block,%20MP_ALIGNMENT,%20PAGE_SIZE);%20//4K
%20%20%20%20if%20(ret)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20struct%20mp_node_s%20*new_node%20=%20(struct%20mp_node_s%20*)%20block;
%20%20%20%20new_node->end%20=%20block%20+%20PAGE_SIZE;
%20%20%20%20new_node->next%20=%20NULL;

%20%20%20%20unsigned%20char%20*ret_addr%20=%20mp_align_ptr(block%20+%20sizeof(struct%20mp_node_s),%20MP_ALIGNMENT);

%20%20%20%20new_node->last%20=%20ret_addr%20+%20size;
%20%20%20%20new_node->quote++;

%20%20%20%20struct%20mp_node_s%20*current%20=%20pool->current;
%20%20%20%20struct%20mp_node_s%20*cur%20=%20NULL;

%20%20%20%20for%20(cur%20=%20current;%20cur->next;%20cur%20=%20cur->next)%20{
%20%20%20%20%20%20%20%20if%20(cur->failed++%20>%204)%20{
%20%20%20%20%20%20%20%20%20%20%20%20current%20=%20cur->next;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20//now%20cur%20=%20last%20node
%20%20%20%20cur->next%20=%20new_node;
%20%20%20%20pool->current%20=%20current;
%20%20%20%20return%20ret_addr;
}

//分配内存
void%20*mp_malloc(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20if%20(size%20<=%200)%20{
%20%20%20%20%20%20%20%20return%20NULL;
%20%20%20%20}
%20%20%20%20if%20(size%20>%20PAGE_SIZE%20-%20sizeof(struct%20mp_node_s))%20{
%20%20%20%20%20%20%20%20//large
%20%20%20%20%20%20%20%20return%20mp_malloc_large(pool,%20size);
%20%20%20%20}
%20%20%20%20else%20{
%20%20%20%20%20%20%20%20//small
%20%20%20%20%20%20%20%20unsigned%20char%20*mem_addr%20=%20NULL;
%20%20%20%20%20%20%20%20struct%20mp_node_s%20*cur%20=%20NULL;
%20%20%20%20%20%20%20%20cur%20=%20pool->current;
%20%20%20%20%20%20%20%20while%20(cur)%20{
%20%20%20%20%20%20%20%20%20%20%20%20mem_addr%20=%20mp_align_ptr(cur->last,%20MP_ALIGNMENT);
%20%20%20%20%20%20%20%20%20%20%20%20if%20(cur->end%20-%20mem_addr%20>=%20size)%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->quote++;//引用+1
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->last%20=%20mem_addr%20+%20size;
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20mem_addr;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20else%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur%20=%20cur->next;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20return%20mp_malloc_block(pool,%20size);//%20open%20new%20space
%20%20%20%20}
}

void%20*mp_calloc(struct%20mp_pool_s%20*pool,%20size_t%20size)%20{
%20%20%20%20void%20*mem_addr%20=%20mp_malloc(pool,%20size);
%20%20%20%20if%20(mem_addr)%20{
%20%20%20%20%20%20%20%20memset(mem_addr,%200,%20size);
%20%20%20%20}
%20%20%20%20return%20mem_addr;
}

//释放内存
void%20mp_free(struct%20mp_pool_s%20*pool,%20void%20*p)%20{
%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{//大块
%20%20%20%20%20%20%20%20if%20(p%20==%20large->alloc)%20{
%20%20%20%20%20%20%20%20%20%20%20%20free(large->alloc);
%20%20%20%20%20%20%20%20%20%20%20%20large->size%20=%200;
%20%20%20%20%20%20%20%20%20%20%20%20large->alloc%20=%20NULL;
%20%20%20%20%20%20%20%20%20%20%20%20return;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20//小块%20引用-1
%20%20%20%20struct%20mp_node_s%20*cur%20=%20NULL;
%20%20%20%20for%20(cur%20=%20pool->head;%20cur;%20cur%20=%20cur->next)%20{
//%20%20%20%20%20%20%20%20printf("cur:%p%20%20%20p:%p%20%20%20end:%pn",%20(unsigned%20char%20*)%20cur,%20(unsigned%20char%20*)%20p,%20(unsigned%20char%20*)%20cur->end);
%20%20%20%20%20%20%20%20if%20((unsigned%20char%20*)%20cur%20<=%20(unsigned%20char%20*)%20p%20&&%20(unsigned%20char%20*)%20p%20<=%20(unsigned%20char%20*)%20cur->end)%20{
%20%20%20%20%20%20%20%20%20%20%20%20cur->quote--;
%20%20%20%20%20%20%20%20%20%20%20%20if%20(cur->quote%20==%200)%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(cur%20==%20pool->head)%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pool->head->last%20=%20(unsigned%20char%20*)%20pool%20+%20sizeof(struct%20mp_pool_s)%20+%20sizeof(struct%20mp_node_s);
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->last%20=%20(unsigned%20char%20*)%20cur%20+%20sizeof(struct%20mp_node_s);
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cur->failed%20=%200;
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pool->current%20=%20pool->head;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20return;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
}

void%20monitor_mp_poll(struct%20mp_pool_s%20*pool,%20char%20*tk)%20{
%20%20%20%20printf("rnrn------start%20monitor%20poll------%srnrn",%20tk);
%20%20%20%20struct%20mp_node_s%20*head%20=%20NULL;
%20%20%20%20int%20i%20=%200;
%20%20%20%20for%20(head%20=%20pool->head;%20head;%20head%20=%20head->next)%20{
%20%20%20%20%20%20%20%20i++;
%20%20%20%20%20%20%20%20if%20(pool->current%20==%20head)%20{
%20%20%20%20%20%20%20%20%20%20%20%20printf("current==>第%d块n",%20i);
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20if%20(i%20==%201)%20{
%20%20%20%20%20%20%20%20%20%20%20%20printf("第%02d块small%20block%20%20已使用:%4ld%20%20剩余空间:%4ld%20%20引用:%4d%20%20failed:%4dn",%20i,
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(unsigned%20char%20*)%20head->last%20-%20(unsigned%20char%20*)%20pool,
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20head->end%20-%20head->last,%20head->quote,%20head->failed);
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20else%20{
%20%20%20%20%20%20%20%20%20%20%20%20printf("第%02d块small%20block%20%20已使用:%4ld%20%20剩余空间:%4ld%20%20引用:%4d%20%20failed:%4dn",%20i,
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(unsigned%20char%20*)%20head->last%20-%20(unsigned%20char%20*)%20head,
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20head->end%20-%20head->last,%20head->quote,%20head->failed);
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20struct%20mp_large_s%20*large;
%20%20%20%20i%20=%200;
%20%20%20%20for%20(large%20=%20pool->large;%20large;%20large%20=%20large->next)%20{
%20%20%20%20%20%20%20%20i++;
%20%20%20%20%20%20%20%20if%20(large->alloc%20!=%20NULL)%20{
%20%20%20%20%20%20%20%20%20%20%20%20printf("第%d块large%20block%20%20size=%dn",%20i,%20large->size);
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20printf("rnrn------stop%20monitor%20poll------rnrn");
}



int%20main()%20{
%20%20%20%20struct%20mp_pool_s%20*p%20=%20mp_create_pool(PAGE_SIZE);
%20%20%20%20monitor_mp_poll(p,%20"create%20memory%20pool");
#if%200
%20%20%20%20printf("mp_align(5,%20%d):%20%d,%20mp_align(17,%20%d):%20%dn",%20MP_ALIGNMENT,%20mp_align(5,%20MP_ALIGNMENT),%20MP_ALIGNMENT,
%20%20%20%20%20%20%20%20%20%20%20mp_align(17,%20MP_ALIGNMENT));
%20%20%20%20printf("mp_align_ptr(p->current,%20%d):%20%p,%20p->current:%20%pn",%20MP_ALIGNMENT,%20mp_align_ptr(p->current,%20MP_ALIGNMENT),
%20%20%20%20%20%20%20%20%20%20%20p->current);
#endif
%20%20%20%20void%20*mp[30];
%20%20%20%20int%20i;
%20%20%20%20for%20(i%20=%200;%20i%20<%2030;%20i++)%20{
%20%20%20%20%20%20%20%20mp[i]%20=%20mp_malloc(p,%20512);
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"申请512字节30个");

%20%20%20%20for%20(i%20=%200;%20i%20<%2030;%20i++)%20{
%20%20%20%20%20%20%20%20mp_free(p,%20mp[i]);
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"销毁512字节30个");

%20%20%20%20int%20j;
%20%20%20%20for%20(i%20=%200;%20i%20<%2050;%20i++)%20{
%20%20%20%20%20%20%20%20char%20*pp%20=%20mp_calloc(p,%2032);
%20%20%20%20%20%20%20%20for%20(j%20=%200;%20j%20<%2032;%20j++)%20{
%20%20%20%20%20%20%20%20%20%20%20%20if%20(pp[j])%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20printf("calloc%20wrongn");
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20exit(-1);
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20}
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"申请32字节50个");

%20%20%20%20for%20(i%20=%200;%20i%20<%2050;%20i++)%20{
%20%20%20%20%20%20%20%20char%20*pp%20=%20mp_malloc(p,%203);
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"申请3字节50个");


%20%20%20%20void%20*pp[10];
%20%20%20%20for%20(i%20=%200;%20i%20<%2010;%20i++)%20{
%20%20%20%20%20%20%20%20pp[i]%20=%20mp_malloc(p,%205120);
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"申请大内存5120字节10个");

%20%20%20%20for%20(i%20=%200;%20i%20<%2010;%20i++)%20{
%20%20%20%20%20%20%20%20mp_free(p,%20pp[i]);
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"销毁大内存5120字节10个");

%20%20%20%20mp_reset_pool(p);
%20%20%20%20monitor_mp_poll(p,%20"reset%20pool");

%20%20%20%20for%20(i%20=%200;%20i%20<%20100;%20i++)%20{
%20%20%20%20%20%20%20%20void%20*s%20=%20mp_malloc(p,%20256);
%20%20%20%20}
%20%20%20%20monitor_mp_poll(p,%20"申请256字节100个");

%20%20%20%20mp_destroy_pool(p);
%20%20%20%20return%200;
}

Nginx内存池对比分析

相关结构体定义对比

创建内存池对比

内存申请对比

 



Tags:内存池   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
高性能低延迟内存池实现技术
调用malloc分配内存大概是微秒级别,高并发低延迟系统的关键路径上,要慎用malloc/new,特别是在线程数量很大的情况下。 给一个测试数据:linux 64位系统,标准库malloc,单线程,gcc开O3...【详细内容】
2023-01-02  Search: 内存池  点击:(182)  评论:(0)  加入收藏
带你用纯C实现一个内存池(图文结合)
为什么要用内存池为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者...【详细内容】
2022-07-29  Search: 内存池  点击:(493)  评论:(0)  加入收藏
C++内存池的简单原理及实现(纯代码解析)
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  Search: 内存池  点击:(488)  评论:(0)  加入收藏
Nginx 内存池似懂非懂?一文带你看清高性能服务器内存池
nginx 内存池 ngx_pool_tnginx 是自己实现了内存池的,所以在nginx ngx_pool_t 这个结构也随处可见,这里主要分析一下内存池的分配逻辑。内存池实现了包括小块内存、大块内存和...【详细内容】
2021-08-03  Search: 内存池  点击:(272)  评论:(0)  加入收藏
C++内存池实现
内存池是自己向OS请求的一大块内存,自己进行管理。##系统调用## 我们先测试系统调用new/delete的用时。#include <iostream>#include <time.h> using namespace std;class Te...【详细内容】
2019-08-28  Search: 内存池  点击:(748)  评论:(0)  加入收藏
▌简易百科推荐
C++中的外部模板及其在当前编译文件中的实例化
在C++中,模板是一种泛型编程的工具,它允许程序员以一种类型无关的方式编写代码。然而,模板的一个常见问题是它们会导致编译时间增加,特别是在大型项目中,当多个源文件包含相同的...【详细内容】
2024-04-11  鲨鱼编程  微信公众号  Tags:C++   点击:(12)  评论:(0)  加入收藏
C++常见避坑指南
C++ 从入门到放弃?本文主要总结了在C++开发或review过程中常见易出错点做了归纳总结,希望借此能增进大家对C++的了解,减少编程出错,提升工作效率,也可以作为C++开发的避坑攻略。...【详细内容】
2024-04-03  腾讯技术工程    Tags:C++   点击:(8)  评论:(0)  加入收藏
C++ 之父反驳白宫警告:自诞生第一天起,C++ 的目标就一直是提高安全性
整理 | 郑丽媛上个月,美国白宫国家网络主任办公室(ONCD)在一份主题为《回到基础构件:通往安全软件之路》的 19 页 PDF 报告中,呼吁开发人员停止使用容易出现内存安全漏洞的编程语...【详细内容】
2024-03-25    CSDN  Tags:C++   点击:(11)  评论:(0)  加入收藏
八个 C++ 开源项目,帮助初学者进阶成长
通过参与或阅读开源项目的源代码,你可以获得丰富的实践机会。实际的项目代码比简单的教程更具挑战性,可以帮助你深入理解 C++ 的各种概念和技术。1.ThreadPool一个简单的 C++1...【详细内容】
2024-03-22  AI让生活更美好  微信公众号  Tags:C++   点击:(27)  评论:(0)  加入收藏
C# 中15个值得收藏的开源项目推荐
在开源的世界里,C# 编程语言也占有一席之地。这些开源项目涵盖了多个领域,从框架、库到工具,它们为C#开发者提供了丰富的资源和工具,帮助他们更高效地开发、测试和部署应用程序...【详细内容】
2024-03-20  程序员编程日记  微信公众号  Tags:C#   点击:(33)  评论:(0)  加入收藏
C#异步编程:Task.Run vs. async-await,掌握基础与高级用法
概述:C#中的异步编程有两主要方式:Task.Run用于在后台线程执行同步操作,而async-await更适用于清晰表达异步流程。基础用法展示了它们的简单应用,高级用法则演示了它们的结合使...【详细内容】
2024-03-09  架构师老卢  今日头条  Tags:C#   点击:(34)  评论:(0)  加入收藏
C++多线程编程:解锁性能与并发的奥秘
今天我们将深入探讨C++中的多线程编程,揭示多线程如何解锁性能潜力,提高程序的并发性能。什么是多线程?在计算机科学中,多线程是指一个进程(程序的执行实例)中的多个线程同时执行...【详细内容】
2024-02-03     AI让生活更美好  Tags:C++   点击:(76)  评论:(0)  加入收藏
C++代码优化攻略
今天我们将深入探讨C++性能优化的世界。在当今软件开发的浪潮中,高性能的代码是必不可少的。无论是开发桌面应用、移动应用,还是嵌入式系统,性能都是关键。1. 选择合适的数据结...【详细内容】
2024-01-26  AI让生活更美好  微信公众号  Tags:C++   点击:(129)  评论:(0)  加入收藏
C# 线程本地存储为什么线程间值不一样
为什么用 ThreadStatic 标记的字段,只有第一个线程拿到了初始值,其他线程都是默认值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给后面类...【详细内容】
2024-01-26  一线码农聊技术  微信公众号  Tags:C#   点击:(80)  评论:(0)  加入收藏
C++质数检测器的设计与实现​
质数,作为数学中的一个基本概念,一直以其独特的性质吸引着众多研究者和爱好者。质数是指大于1的自然数中,除了1和它本身以外不再有其他因数的数。在实际应用中,质数检测也扮演着...【详细内容】
2024-01-15  鲨鱼编程  微信公众号  Tags:C++   点击:(124)  评论:(0)  加入收藏
站内最新
站内热门
站内头条