You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
12 KiB
392 lines
12 KiB
|
3 days ago
|
/******************************************************************************
|
||
|
|
|
||
|
|
版权所有 (C), 2018-2099, Radkil
|
||
|
|
|
||
|
|
******************************************************************************
|
||
|
|
文 件 名 : rd_mempool.c
|
||
|
|
版 本 号 : 初稿
|
||
|
|
作 者 : Radkil
|
||
|
|
生成日期 : 2026年3月10日星期二
|
||
|
|
最近修改 :
|
||
|
|
功能描述 : 内存池模块
|
||
|
|
|
||
|
|
修改历史 :
|
||
|
|
1.日 期 : 2026年3月10日星期二
|
||
|
|
作 者 : Radkil
|
||
|
|
修改内容 : 创建文件
|
||
|
|
|
||
|
|
******************************************************************************/
|
||
|
|
#include "rd_mempool.h"
|
||
|
|
#include "rd_thread.h"
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 外部变量说明 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 外部函数原型说明 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 内部函数原型说明 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 全局变量 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
// 内存块头部结构 (隐藏在用户指针之前)
|
||
|
|
// 这个结构体的大小必须是 ALIGN_SIZE 的倍数
|
||
|
|
typedef struct MemBlockHeader {
|
||
|
|
size_t size; // 当前块的总大小 (包含 Header 本身)
|
||
|
|
int is_free; // 1 = 空闲, 0 = 已占用
|
||
|
|
struct MemBlockHeader* next_free; // 指向下一个空闲块 (仅在 is_free=1 时有效)
|
||
|
|
struct MemBlockHeader* prev_free; // 指向上一个空闲块 (双向链表便于删除节点)
|
||
|
|
} MemBlockHeader_t;
|
||
|
|
|
||
|
|
#define HEADER_SIZE RD_ALIGN(sizeof(MemBlockHeader_t), RD_ALIGN_SIZE)
|
||
|
|
#define MIN_BLOCK_SIZE (HEADER_SIZE + MIN_ALLOC_UNIT)
|
||
|
|
|
||
|
|
static uint8_t* g_pool_start = NULL;
|
||
|
|
static uint8_t* g_pool_end = NULL;
|
||
|
|
static MemBlockHeader_t* g_free_list_head = NULL;
|
||
|
|
static int g_initialized = 0;
|
||
|
|
|
||
|
|
#ifdef USE_HEAP_FOR_MEMPOOL
|
||
|
|
static uint8_t* g_heap_buf = NULL;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 模块级变量 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 常量定义 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
|
||
|
|
/*----------------------------------------------*
|
||
|
|
* 宏定义 *
|
||
|
|
*----------------------------------------------*/
|
||
|
|
|
||
|
|
// 获取用户数据指针
|
||
|
|
rd_inline void* get_user_ptr(MemBlockHeader_t* block)
|
||
|
|
{
|
||
|
|
return (void*)((uint8_t*)block + HEADER_SIZE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取头部指针
|
||
|
|
rd_inline MemBlockHeader_t* get_header_ptr(void* ptr)
|
||
|
|
{
|
||
|
|
return (MemBlockHeader_t*)((uint8_t*)ptr - HEADER_SIZE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查指针是否在池内
|
||
|
|
rd_inline int is_valid_ptr(void* ptr)
|
||
|
|
{
|
||
|
|
if (!g_initialized || !ptr) return 0;
|
||
|
|
return ((uint8_t*)ptr >= (g_pool_start + HEADER_SIZE) &&
|
||
|
|
(uint8_t*)ptr < g_pool_end);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 从空闲链表中移除节点
|
||
|
|
static void remove_from_free_list(MemBlockHeader_t* block)
|
||
|
|
{
|
||
|
|
if (!block->is_free) return;
|
||
|
|
|
||
|
|
if (block->prev_free)
|
||
|
|
{
|
||
|
|
block->prev_free->next_free = block->next_free;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
g_free_list_head = block->next_free;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (block->next_free)
|
||
|
|
{
|
||
|
|
block->next_free->prev_free = block->prev_free;
|
||
|
|
}
|
||
|
|
|
||
|
|
block->next_free = NULL;
|
||
|
|
block->prev_free = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 添加节点到空闲链表头部 (简单高效)
|
||
|
|
static void add_to_free_list(MemBlockHeader_t* block)
|
||
|
|
{
|
||
|
|
block->is_free = 1;
|
||
|
|
block->next_free = g_free_list_head;
|
||
|
|
block->prev_free = NULL;
|
||
|
|
|
||
|
|
if (g_free_list_head)
|
||
|
|
{
|
||
|
|
g_free_list_head->prev_free = block;
|
||
|
|
}
|
||
|
|
g_free_list_head = block;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 物理相邻的块合并 (关键步骤:消除碎片)
|
||
|
|
static void merge_free_blocks(MemBlockHeader_t* block)
|
||
|
|
{
|
||
|
|
if (!block || !block->is_free) return;
|
||
|
|
|
||
|
|
// 1. 尝试与【后一个】块合并
|
||
|
|
// 计算下一个块的地址
|
||
|
|
uint8_t* next_block_addr = (uint8_t*)block + block->size;
|
||
|
|
|
||
|
|
// 检查是否越界
|
||
|
|
if (next_block_addr < g_pool_end)
|
||
|
|
{
|
||
|
|
MemBlockHeader_t* next_block = (MemBlockHeader_t*)next_block_addr;
|
||
|
|
|
||
|
|
// 如果下一个块也是空闲的,合并!
|
||
|
|
if (next_block->is_free)
|
||
|
|
{
|
||
|
|
// 从链表中移除下一个块 (因为要合并进当前块)
|
||
|
|
remove_from_free_list(next_block);
|
||
|
|
|
||
|
|
// 扩大当前块尺寸
|
||
|
|
block->size += next_block->size;
|
||
|
|
|
||
|
|
// 注意:不需要重新设置 next_block 的 header,因为它已经被吞并了
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. 尝试与【前一个】块合并
|
||
|
|
// 由于单向无法直接得知前一个块,我们需要遍历或者维护额外信息。
|
||
|
|
// 优化技巧:在 Free 列表中遍历时,我们无法直接知道物理前驱。
|
||
|
|
// 简单做法:遍历整个空闲链表,看谁的 (addr + size) == 当前 addr。
|
||
|
|
// 为了效率,通常我们在分配时保证顺序,或者接受 O(N) 的前向查找。
|
||
|
|
// 这里为了代码简洁和稳健,采用遍历查找前驱 (在碎片不多时很快)。
|
||
|
|
|
||
|
|
MemBlockHeader_t* prev_block = g_free_list_head;
|
||
|
|
while (prev_block) {
|
||
|
|
if (prev_block == block) {
|
||
|
|
prev_block = prev_block->next_free;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint8_t* potential_next = (uint8_t*)prev_block + prev_block->size;
|
||
|
|
if (potential_next == (uint8_t*)block)
|
||
|
|
{
|
||
|
|
// 发现前驱是空闲的,合并:前驱吞并当前
|
||
|
|
remove_from_free_list(block); // 先移除当前
|
||
|
|
remove_from_free_list(prev_block); // 移除前驱准备重组
|
||
|
|
|
||
|
|
prev_block->size += block->size;
|
||
|
|
add_to_free_list(prev_block); // 重新加入链表
|
||
|
|
return; // 合并完成
|
||
|
|
}
|
||
|
|
prev_block = prev_block->next_free;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK void Rd_MemDoInit(void)
|
||
|
|
{
|
||
|
|
if (g_initialized) return;
|
||
|
|
|
||
|
|
#ifdef USE_HEAP_FOR_MEMPOOL
|
||
|
|
g_heap_buf = (uint8_t*)MP_MALLOC(MEM_POOL_TOTAL_SIZE);
|
||
|
|
if (!g_heap_buf)
|
||
|
|
{
|
||
|
|
log_e("MemPool Init Failed: Malloc error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
g_pool_start = g_heap_buf;
|
||
|
|
#else
|
||
|
|
// 静态数组,确保对齐
|
||
|
|
static uint8_t static_pool[MEM_POOL_TOTAL_SIZE] __attribute__((aligned(RD_ALIGN_SIZE)));
|
||
|
|
g_pool_start = static_pool;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
g_pool_end = g_pool_start + MEM_POOL_TOTAL_SIZE;
|
||
|
|
|
||
|
|
// 初始化整个池为一个巨大的空闲块
|
||
|
|
MemBlockHeader_t* initial_block = (MemBlockHeader_t*)g_pool_start;
|
||
|
|
initial_block->size = MEM_POOL_TOTAL_SIZE;
|
||
|
|
initial_block->is_free = 1;
|
||
|
|
initial_block->next_free = NULL;
|
||
|
|
initial_block->prev_free = NULL;
|
||
|
|
|
||
|
|
g_free_list_head = initial_block;
|
||
|
|
g_initialized = 1;
|
||
|
|
|
||
|
|
log_d("MemPool Init Success: Total %d Bytes", MEM_POOL_TOTAL_SIZE);
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK void Rd_MemDoDeinit(void)
|
||
|
|
{
|
||
|
|
#ifdef USE_HEAP_FOR_MEMPOOL
|
||
|
|
if (g_heap_buf)
|
||
|
|
{
|
||
|
|
MP_FREE(g_heap_buf);
|
||
|
|
g_heap_buf = NULL;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
g_initialized = 0;
|
||
|
|
g_free_list_head = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK void* Rd_MemPoolMalloc(size_t size)
|
||
|
|
{
|
||
|
|
if (!g_initialized || size == 0) return NULL;
|
||
|
|
|
||
|
|
void *pvReturn = NULL;
|
||
|
|
Rd_MutexLock(NULL, __func__, NULL);
|
||
|
|
{
|
||
|
|
|
||
|
|
// 对齐大小计算:用户数据 + 头部
|
||
|
|
size_t aligned_size = RD_ALIGN(size, RD_ALIGN_SIZE);
|
||
|
|
size_t total_needed = HEADER_SIZE + aligned_size;
|
||
|
|
|
||
|
|
// 确保不小于最小块大小
|
||
|
|
if (total_needed < MIN_BLOCK_SIZE)
|
||
|
|
{
|
||
|
|
total_needed = MIN_BLOCK_SIZE;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 首次适应算法 (First Fit)
|
||
|
|
MemBlockHeader_t* current = g_free_list_head;
|
||
|
|
while (current)
|
||
|
|
{
|
||
|
|
if (current->size >= total_needed)
|
||
|
|
{
|
||
|
|
// 找到合适的块
|
||
|
|
|
||
|
|
// 检查是否可以切分 (剩下的部分是否足够形成一个新块)
|
||
|
|
size_t remaining = current->size - total_needed;
|
||
|
|
|
||
|
|
if (remaining >= MIN_BLOCK_SIZE)
|
||
|
|
{
|
||
|
|
// 【切分操作】
|
||
|
|
// 1. 创建新块 (在剩余空间)
|
||
|
|
MemBlockHeader_t* new_block = (MemBlockHeader_t*)((uint8_t*)current + total_needed);
|
||
|
|
new_block->size = remaining;
|
||
|
|
new_block->is_free = 0; // 暂时设为占用,稍后加入链表
|
||
|
|
|
||
|
|
// 2. 缩小当前块
|
||
|
|
current->size = total_needed;
|
||
|
|
|
||
|
|
// 3. 将新块加入空闲链表
|
||
|
|
add_to_free_list(new_block);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// 不切分,整个块都给用户 (避免产生极小碎片)
|
||
|
|
// 剩余空间被浪费在当前块内,直到下次 Free
|
||
|
|
}
|
||
|
|
|
||
|
|
// 从空闲链表移除当前块,标记为占用
|
||
|
|
remove_from_free_list(current);
|
||
|
|
current->is_free = 0;
|
||
|
|
current->next_free = NULL;
|
||
|
|
current->prev_free = NULL;
|
||
|
|
|
||
|
|
pvReturn = get_user_ptr(current);
|
||
|
|
}
|
||
|
|
current = current->next_free;
|
||
|
|
}
|
||
|
|
if (pvReturn == NULL)
|
||
|
|
{
|
||
|
|
log_d("MemPool Malloc Failed: Request %zu, No Block Found", size);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Rd_MutexUnlock(NULL, __func__, NULL);
|
||
|
|
return pvReturn;
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK void Rd_MemPoolFree(void* ptr)
|
||
|
|
{
|
||
|
|
if (!is_valid_ptr(ptr))
|
||
|
|
{
|
||
|
|
log_d("MemPool Free Error: Invalid Pointer %p", ptr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
Rd_MutexLock(NULL, __func__, NULL);
|
||
|
|
{
|
||
|
|
MemBlockHeader_t* block = get_header_ptr(ptr);
|
||
|
|
|
||
|
|
// 双重释放保护
|
||
|
|
if (block->is_free)
|
||
|
|
{
|
||
|
|
log_d("MemPool Free Error: Double Free detected at %p", ptr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 标记为空闲并加入链表
|
||
|
|
add_to_free_list(block);
|
||
|
|
|
||
|
|
// 【关键】尝试与相邻空闲块合并,减少碎片
|
||
|
|
merge_free_blocks(block);
|
||
|
|
}
|
||
|
|
Rd_MutexUnlock(NULL, __func__, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK void* Rd_MemPoolCalloc(size_t n, size_t size)
|
||
|
|
{
|
||
|
|
size_t total = n * size;
|
||
|
|
void* ptr = Rd_MemPoolMalloc(total);
|
||
|
|
if (ptr)
|
||
|
|
{
|
||
|
|
MP_MEMSET(ptr, 0, total);
|
||
|
|
}
|
||
|
|
return ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK void* Rd_MemPoolRealloc(void* ptr, size_t new_size)
|
||
|
|
{
|
||
|
|
if (!ptr) return Rd_MemPoolMalloc(new_size);
|
||
|
|
if (new_size == 0)
|
||
|
|
{
|
||
|
|
Rd_MemPoolFree(ptr);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
MemBlockHeader_t* block = get_header_ptr(ptr);
|
||
|
|
size_t old_user_size = block->size - HEADER_SIZE;
|
||
|
|
|
||
|
|
if (old_user_size >= new_size)
|
||
|
|
{
|
||
|
|
// 现有块足够大,不需要移动 (可选:这里可以不做任何事,或者返回原指针)
|
||
|
|
// 注意:标准 realloc 在缩小时行为未定义,通常不缩小块,除非为了合并
|
||
|
|
return ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 需要扩大:分配新的,拷贝,释放旧的
|
||
|
|
void* new_ptr = Rd_MemPoolMalloc(new_size);
|
||
|
|
if (!new_ptr) return NULL;
|
||
|
|
|
||
|
|
MP_MEMCPY(new_ptr, ptr, old_user_size);
|
||
|
|
Rd_MemPoolFree(ptr);
|
||
|
|
return new_ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK size_t Rd_MemPoolGetFreeCount(void)
|
||
|
|
{
|
||
|
|
if (!g_initialized) return 0;
|
||
|
|
size_t total_free = 0;
|
||
|
|
MemBlockHeader_t* curr = g_free_list_head;
|
||
|
|
while (curr)
|
||
|
|
{
|
||
|
|
total_free += (curr->size - HEADER_SIZE);
|
||
|
|
curr = curr->next_free;
|
||
|
|
}
|
||
|
|
return total_free;
|
||
|
|
}
|
||
|
|
|
||
|
|
WEAK size_t Rd_MemPoolGetTotalCount(void)
|
||
|
|
{
|
||
|
|
return (size_t)MEM_POOL_TOTAL_SIZE;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Rd_MemPoolInit_Auto(void)
|
||
|
|
{
|
||
|
|
Rd_MemDoInit();
|
||
|
|
}
|
||
|
|
|
||
|
|
__attribute__((destructor(102)))
|
||
|
|
void Rd_MemPoolDeinit_Auto(void)
|
||
|
|
{
|
||
|
|
Rd_MemDoDeinit();
|
||
|
|
}
|
||
|
|
|