高性能服务器---内存池设计

    技术2022-07-10  117

    文章链接:https://codemouse.online/archives/2020-06-26-23-50-33

    内存池

    在日常的写代码中,经常有需要申请内存的时候,但是频繁的申请释放会特别的浪费时间,于是衍生了内存池,由内存池帮忙管理内存,内存池帮忙统一释放,免去了用户的频繁释放,申请内存只需要从内存池中已经申请好的内存中取出,如果没有大于需求的内存,则内存池再去申请一块回来。

    内存池增强了程序员对内存碎片话的管理,加快了内存的使用速度。

    这个内存池的模型是参考了nginx内使用的内存池,相当于一个简化版,仅供参考。

    本例子中,暂时没有写对小块内存的释放处理,只有在摧毁内存池的时候,才会去释放小块内存,这个还待优化。在大块内存中,用完就可以直接释放掉,不用加以保留。

    小块内存其实可以根据使用的剩余大小做一个限制,超过一定界限,再通过设定一个flag,当没人用的时候就设定为0,这时候将内存初始化,清零一下,相当于空出一块内存,省去了申请的过程。有兴趣的可以实现一下。我说的flag也就是我小块内存的failed变量。

    模型图

    左边是小块内存的管理,右边是大块内存的管理。

    实现代码

    #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #define MP_ALIGNMENT 32 #define MP_PAGE_SIZE 4096 #define MP_MAX_ALLOC_FROM_POOL (MP_PAGE_SIZE-1) #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_large_s { struct mp_large_s *next; void *alloc; }; // 小块内存,用一部分,取一部分,全部无效,统一释放 struct mp_node_s { unsigned char *last; unsigned char *end; struct mp_node_s *next; // 下一块小块内存的指针 size_t failed; // 记录有几块内存在使用 }; struct mp_pool_s { size_t max; // 区分大块与小块内存的界限 struct mp_node_s *current; struct mp_large_s *large; struct mp_node_s head[0]; }; struct mp_pool_s *mp_create_pool(size_t size); void mp_destory_pool(struct mp_pool_s *pool); void *mp_alloc(struct mp_pool_s *pool, size_t size); void *mp_nalloc(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); // 创建内存池 struct mp_pool_s *mp_create_pool(size_t size) { struct mp_pool_s *p; // 分配池和小块内存的头和小块内存 int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s)); if (ret) { return NULL; } p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL; p->current = p->head; p->large = NULL; p->head->last = (unsigned char *)p + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s); p->head->end = p->head->last + size; p->head->failed = 0; return p; } // 内存池的摧毁 void mp_destory_pool(struct mp_pool_s *pool) { struct mp_node_s *h, *n; struct mp_large_s *l; for (l = pool->large; l; l = l->next) { if (l->alloc) { free(l->alloc); } } h = pool->head->next; while (h) { n = h->next; free(h); h = n; } free(pool); } // 重置内存池,释放大块内存,小块内存保留,链表节点保留 void mp_reset_pool(struct mp_pool_s *pool) { struct mp_node_s *h; struct mp_large_s *l; for (l = pool->large; l; l = l->next) { if (l->alloc) { free(l->alloc); } } pool->large = NULL; for (h = pool->head; h; h = h->next) { h->last = (unsigned char *)h + sizeof(struct mp_node_s); // memset(h->last, h->end - h->last); } } // 小块内存的申请 static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) { unsigned char *m; struct mp_node_s *h = pool->head; size_t psize = (size_t)(h->end - (unsigned char *)h); int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize); if (ret) return NULL; struct mp_node_s *p, *new_node, *current; new_node = (struct mp_node_s*)m; new_node->end = m + psize; new_node->next = NULL; new_node->failed = 0; m += sizeof(struct mp_node_s); // 矫正地址,做好地址对齐 m = mp_align_ptr(m, MP_ALIGNMENT); new_node->last = m + size; // 头插法插入小块内存结点 current = current = pool->current; new_node->next = current; pool->current = new_node; return m; } // 申请大块内存 static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) { //void *p = malloc(size); //if (p == NULL) return NULL; void *p; int ret = posix_memalign(&p, MP_ALIGNMENT, size); if (ret) { return NULL; } size_t n = 0; struct mp_large_s *large; for (large = pool->large; large; large = large->next) { if (large->alloc == NULL) { large->alloc = p; return p; } } // 没找到空闲节点,去申请新节点,如果此节点大小大于初始化的max大小,就会函数嵌套循环(bug) large = mp_alloc(pool, sizeof(struct mp_large_s)); if (large == NULL) { free(p); return NULL; } // 头插法插入大块内存节点 large->alloc = p; large->next = pool->large; pool->large = large; return p; } // 获取大块内存,有地址对齐 void *mp_memalign(struct mp_pool_s *pool, size_t size, size_t alignment) { void *p; int ret = posix_memalign(&p, alignment, size); if (ret) { return NULL; } struct mp_large_s *large = mp_alloc(pool, sizeof(struct mp_large_s)); if (large == NULL) { free(p); return NULL; } // 头插法插入 large->alloc = p; large->next = pool->large; pool->large = large; return p; } // 获取内存地址,并返回一个对齐的地址 void *mp_alloc(struct mp_pool_s *pool, size_t size) { unsigned char *m; struct mp_node_s *p; if (size <= pool->max) { p = pool->current; do { m = mp_align_ptr(p->last, MP_ALIGNMENT); if ((size_t)(p->end - m) >= size) { p->last = m + size; return m; } p = p->next; } while (p); return mp_alloc_block(pool, size); } return mp_alloc_large(pool, size); } // 获取内存,但是没有地址对齐 void *mp_nalloc(struct mp_pool_s *pool, size_t size) { unsigned char *m; struct mp_node_s *p; if (size <= pool->max) { p = pool->current; do { m = p->last; if ((size_t)(p->end - m) >= size) { p->last = m+size; return m; } p = p->next; } while (p); return mp_alloc_block(pool, size); } return mp_alloc_large(pool, size); } // 申请内存,并清零 void *mp_calloc(struct mp_pool_s *pool, size_t size) { void *p = mp_alloc(pool, size); if (p) { memset(p, 0, size); } return p; } // 释放大块内存,但是没有处理小块内存 void mp_free(struct mp_pool_s *pool, void *p) { struct mp_large_s *l; for (l = pool->large; l; l = l->next) { if (p == l->alloc) { free(l->alloc); l->alloc = NULL; return ; } } }

    使用范例

    int main(int argc, char *argv[]) { int size = 1 << 12; // 4096 struct mp_pool_s *p = mp_create_pool(size); int i = 0; for (i = 0;i < 10;i ++) { void *mp = mp_alloc(p, 512); // mp_free(mp); } //printf("mp_create_pool: %ld\n", p->max); printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n", mp_align(24, 32), mp_align(17, 32)); //printf("mp_align_ptr(p->current, 32): %lx, p->current: %lx, mp_align(p->large, 32): %lx, p->large: %lx\n", mp_align_ptr(p->current, 32), p->current, mp_align_ptr(p->large, 32), p->large); int j = 0; for (i = 0;i < 5;i ++) { char *pp = mp_calloc(p, 32); for (j = 0;j < 32;j ++) { if (pp[j]) { printf("calloc wrong\n"); } printf("calloc success\n"); } } //printf("mp_reset_pool\n"); for (i = 0;i < 5;i ++) { void *l = mp_alloc(p, 8192); mp_free(p, l); } mp_reset_pool(p); //printf("mp_destory_pool\n"); for (i = 0;i < 58;i ++) { mp_alloc(p, 256); } mp_destory_pool(p); return 0; }
    Processed: 0.017, SQL: 12