自实现内存池:按大小分桶,8bit跨度,支持释放。
resp协议pipline测试。
This commit is contained in:
316
memory/mempool.c
Normal file
316
memory/mempool.c
Normal file
@@ -0,0 +1,316 @@
|
||||
|
||||
|
||||
|
||||
|
||||
#include "mempool.h"
|
||||
|
||||
|
||||
|
||||
static inline void* page_payload(mp_page_t *pg){
|
||||
return (void*)((char*)pg + sizeof(mp_page_t));
|
||||
}
|
||||
|
||||
static inline void* large_page_payload(mp_large_t *large){
|
||||
return (void*)((char*)large + sizeof(mp_large_t));
|
||||
}
|
||||
|
||||
static inline mp_page_t* ptr_to_page(void* p){
|
||||
return (mp_page_t*)((uintptr_t)p & ~(MEMPOOL_PAGE_SIZE - 1));
|
||||
}
|
||||
|
||||
static inline int size_to_index(size_t size){
|
||||
if(size == 0 || size > MEMPOOL_BLOCK_MAX_SIZE) return -1;
|
||||
|
||||
size_t aligned = (size + 7) & ~7; // 向上8字节对齐
|
||||
return (aligned >> 3) - 1;
|
||||
}
|
||||
|
||||
|
||||
// bitmap 操作函数
|
||||
static inline void bitmap_set(uint64_t *bitmap, uint16_t index){
|
||||
bitmap[index / 64] |= (1ULL << (index % 64));
|
||||
}
|
||||
|
||||
static inline void bitmap_clear(uint64_t *bitmap, uint16_t index){
|
||||
bitmap[index / 64] &= ~(1ULL << (index % 64));
|
||||
}
|
||||
|
||||
static inline int bitmap_test(uint64_t *bitmap, uint16_t index){
|
||||
return (bitmap[index / 64] & (1ULL << (index % 64))) != 0;
|
||||
}
|
||||
|
||||
static inline void bitmap_clear_all(uint64_t *bitmap, size_t size){
|
||||
memset(bitmap, 0, size * sizeof(uint64_t));
|
||||
}
|
||||
|
||||
// 根据指针计算在页中的块索引
|
||||
static inline uint16_t ptr_to_block_index(mp_page_t *pg, void *ptr){
|
||||
char *base = (char*)page_payload(pg);
|
||||
char *p = (char*)ptr;
|
||||
size_t offset = p - base;
|
||||
return (uint16_t)(offset / pg->owner->block_size);
|
||||
}
|
||||
|
||||
|
||||
static mp_page_t* mp_page_create(mp_bucket_t *owner){
|
||||
if(!owner) return NULL;
|
||||
|
||||
void *mem = NULL;
|
||||
mem = aligned_alloc(MEMPOOL_PAGE_SIZE, MEMPOOL_PAGE_SIZE);
|
||||
if(!mem) return NULL;
|
||||
|
||||
mp_page_t *pg = (mp_page_t*) mem;
|
||||
|
||||
size_t usable = MEMPOOL_PAGE_SIZE - sizeof(mp_page_t);
|
||||
uint16_t cap = (uint16_t)(usable / owner->block_size);
|
||||
if(cap == 0) {
|
||||
free(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pg->owner = owner;
|
||||
pg->capacity = cap;
|
||||
pg->free_count = cap;
|
||||
pg->prev = NULL;
|
||||
pg->next = NULL;
|
||||
|
||||
bitmap_clear_all(pg->bitmap, 8);
|
||||
|
||||
char *p = (char*)page_payload(pg);
|
||||
for(uint16_t i = 0;i < cap - 1; ++ i){
|
||||
*(void**)p = (void*)(p + owner->block_size);
|
||||
p = p + owner->block_size;
|
||||
}
|
||||
*(void**)p = NULL;
|
||||
pg->free_list = page_payload(pg);
|
||||
|
||||
return pg;
|
||||
}
|
||||
|
||||
static void *mp_page_alloc(mp_page_t *pg){
|
||||
if(!pg || !pg->free_list || pg->free_count == 0) return NULL;
|
||||
|
||||
void *ret = pg->free_list;
|
||||
pg->free_list = *(void**)ret;
|
||||
pg->free_count --;
|
||||
|
||||
// 标记该块为已分配
|
||||
uint16_t index = ptr_to_block_index(pg, ret);
|
||||
bitmap_set(pg->bitmap, index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mp_page_free(mp_page_t *pg, void *ptr){
|
||||
if(!pg || !ptr) return MEMPOOL_INVALID_INPUT;
|
||||
|
||||
// 检查是否是 double free
|
||||
uint16_t index = ptr_to_block_index(pg, ptr);
|
||||
if(!bitmap_test(pg->bitmap, index)){
|
||||
// 该块未被分配,可能是 double free
|
||||
return MEMPOOL_DOUBLE_FREE;
|
||||
}
|
||||
|
||||
// 标记该块为空闲
|
||||
bitmap_clear(pg->bitmap, index);
|
||||
|
||||
*(void**)ptr = pg->free_list;
|
||||
pg->free_list = ptr;
|
||||
pg->free_count ++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mp_create(mp_pool_t *pool){
|
||||
if(!pool) return MEMPOOL_INVALID_INPUT;
|
||||
for(int i = 0;i < MEMPOOL_NUM_CLASSES; ++ i){
|
||||
mp_bucket_t *bucket = &pool->buckets[i];
|
||||
bucket->block_size = (i+1)*MEMPOOL_ALIGNMENT;
|
||||
bucket->page_count = 0;
|
||||
bucket->empty_count = 0;
|
||||
bucket->empty_pages = bucket->partial_pages = bucket->full_pages = NULL;
|
||||
}
|
||||
pool->large_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp_destroy(mp_pool_t *pool){
|
||||
if(!pool) return MEMPOOL_INVALID_INPUT;
|
||||
|
||||
// 释放所有大块内存
|
||||
mp_large_t *large = pool->large_list;
|
||||
while(large){
|
||||
mp_large_t *next = large->next;
|
||||
free(large);
|
||||
large = next;
|
||||
}
|
||||
|
||||
// 释放所有页面
|
||||
for(int i = 0; i < MEMPOOL_NUM_CLASSES; i++){
|
||||
mp_bucket_t *bucket = &pool->buckets[i];
|
||||
mp_page_t *lists[] = {bucket->empty_pages, bucket->partial_pages, bucket->full_pages};
|
||||
for(int j = 0; j < 3; j++){
|
||||
mp_page_t *pg = lists[j];
|
||||
while(pg){
|
||||
mp_page_t *next = pg->next;
|
||||
free(pg);
|
||||
pg = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *mp_alloc(mp_pool_t *pool, size_t size){
|
||||
if(!pool) return NULL;
|
||||
if(size > MEMPOOL_BLOCK_MAX_SIZE){
|
||||
// large_page
|
||||
void *mem = malloc(size + sizeof(mp_large_t));
|
||||
if(!mem) return NULL;
|
||||
|
||||
mp_large_t *large_page = (mp_large_t*) mem;
|
||||
|
||||
large_page->prev = NULL;
|
||||
large_page->next = pool->large_list;
|
||||
if (pool->large_list) pool->large_list->prev = large_page;
|
||||
pool->large_list = large_page;
|
||||
|
||||
return large_page_payload(large_page);
|
||||
}
|
||||
|
||||
int i = size_to_index(size);
|
||||
if(i == -1) return NULL;
|
||||
|
||||
mp_bucket_t *bucket = &pool->buckets[i];
|
||||
|
||||
if(!bucket->partial_pages){
|
||||
if(!bucket->empty_pages){
|
||||
// 无空页,重新分配
|
||||
mp_page_t *pg = mp_page_create(bucket);
|
||||
if(!pg) return NULL;
|
||||
bucket->partial_pages = pg;
|
||||
bucket->page_count ++;
|
||||
}else{
|
||||
// 有空页,使用空页
|
||||
mp_page_t *epage = bucket->empty_pages;
|
||||
bucket->empty_pages = epage->next;
|
||||
if(bucket->empty_pages) bucket->empty_pages->prev = NULL;
|
||||
epage->next = NULL;
|
||||
epage->prev = NULL;
|
||||
|
||||
bucket->partial_pages = epage;
|
||||
bucket->empty_count --;
|
||||
}
|
||||
}
|
||||
|
||||
mp_page_t *pg = bucket->partial_pages;
|
||||
void *p = mp_page_alloc(pg);
|
||||
if(!p) return NULL;
|
||||
|
||||
if(pg->free_count == 0){
|
||||
// 该页没有空位
|
||||
// 从partial链表中取出
|
||||
if(pg == bucket->partial_pages){
|
||||
bucket->partial_pages = pg->next;
|
||||
}
|
||||
if(pg->prev != NULL) pg->prev->next = pg->next;
|
||||
if(pg->next != NULL) pg->next->prev = pg->prev;
|
||||
pg->prev = NULL;
|
||||
pg->next = NULL;
|
||||
|
||||
// 移入 fullpage
|
||||
if(!bucket->full_pages) {
|
||||
bucket->full_pages = pg;
|
||||
}else{
|
||||
pg->prev = NULL;
|
||||
pg->next = bucket->full_pages;
|
||||
if (bucket->full_pages) bucket->full_pages->prev = pg;
|
||||
bucket->full_pages = pg;
|
||||
}
|
||||
}else {
|
||||
// 不做处理
|
||||
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int mp_free(mp_pool_t *pool, void *ptr){
|
||||
if(!pool || !ptr) return MEMPOOL_INVALID_INPUT;
|
||||
mp_large_t *large = pool->large_list;
|
||||
while(large){
|
||||
if(ptr == large_page_payload(large)){
|
||||
// 从large链表中删除
|
||||
if(large == pool->large_list) pool->large_list = large->next;
|
||||
if(large->prev) large->prev->next = large->next;
|
||||
if(large->next) large->next->prev = large->prev;
|
||||
large->prev = NULL;
|
||||
large->next = NULL;
|
||||
|
||||
free(large);
|
||||
return 0;
|
||||
}
|
||||
large = large->next;
|
||||
}
|
||||
|
||||
mp_page_t *pg = ptr_to_page(ptr);
|
||||
|
||||
uint16_t before = pg->free_count;
|
||||
int ret = mp_page_free(pg, ptr);
|
||||
if(ret != 0){
|
||||
printf("mempool error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if(pg->free_count >= pg->capacity){
|
||||
// free后成为完全空闲页
|
||||
mp_bucket_t *bucket = pg->owner;
|
||||
if(bucket->empty_count > MEMPOOL_CACHE_PAGE){
|
||||
// 空闲页很多
|
||||
// 摘出partial链表
|
||||
if(pg == bucket->partial_pages) bucket->partial_pages = pg->next;
|
||||
if(pg->prev != NULL) pg->prev->next = pg->next;
|
||||
if(pg->next != NULL) pg->next->prev = pg->prev;
|
||||
pg->prev = NULL;
|
||||
pg->next = NULL;
|
||||
// 释放
|
||||
free(pg);
|
||||
bucket->page_count --;
|
||||
}else{
|
||||
// 空闲页并不多
|
||||
// 摘出partial链表
|
||||
if(pg == bucket->partial_pages) bucket->partial_pages = pg->next;
|
||||
if(pg->prev != NULL) pg->prev->next = pg->next;
|
||||
if(pg->next != NULL) pg->next->prev = pg->prev;
|
||||
pg->prev = NULL;
|
||||
pg->next = NULL;
|
||||
// 加入empty链表
|
||||
pg->next = bucket->empty_pages;
|
||||
if(bucket->empty_pages) bucket->empty_pages->prev = pg;
|
||||
bucket->empty_pages = pg;
|
||||
bucket->empty_count ++;
|
||||
}
|
||||
|
||||
}else if(before == 0 && pg->free_count == 1){
|
||||
// free 后需要从full移动到partial
|
||||
mp_bucket_t *bucket = pg->owner;
|
||||
// 移出full链表
|
||||
if(pg == bucket->full_pages) bucket->full_pages = pg->next;
|
||||
if(pg->prev != NULL) pg->prev->next = pg->next;
|
||||
if(pg->next != NULL) pg->next->prev = pg->prev;
|
||||
pg->prev = NULL;
|
||||
pg->next = NULL;
|
||||
|
||||
// 加入partial
|
||||
pg->prev = NULL;
|
||||
pg->next = bucket->partial_pages;
|
||||
if (bucket->partial_pages) bucket->partial_pages->prev = pg;
|
||||
bucket->partial_pages = pg;
|
||||
}else {
|
||||
// 有空余空间的页不做处理
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user