AI算法移植与应用架构之内存管理模块高复用可移植
第一部分 架构
项目模块目录:

1. include/mm.h
/**
* @file mm.h
* @brief 内存管理模块 - 独立内存池管理系统
* @author Memory Manager Team
* @version 2.0.0
*
* @section 设计原则 (内存管理七字诀)
* ① 预分配,不动态 所有内存池在编译时或初始化时一次性分配
* ② 定大小,不分片 每个池的槽位大小固定,避免内存碎片
* ③ 池化管理,零碎片 使用槽位池管理,分配释放无碎片
* ④ 谁申请,谁释放 明确所有权,申请者负责释放
* ⑤ 引用计数,防泄漏 支持引用计数,自动管理共享内存
* ⑥ 对齐边界,性能优 内存按64字节对齐,缓存友好
* ⑦ 栈优先,堆次之 小对象用栈,大对象用池
* ⑧ 静态区,放常量 配置信息放在只读数据段
*
* @section 使用示例
* @code
* // 初始化
* mm_init();
*
* // 从张量池分配内存
* void* data = mm_alloc(MM_POOL_TENSOR, 1024);
*
* // 使用数据
* memset(data, 0, 1024);
*
* // 增加引用计数(共享)
* mm_ref(data);
*
* // 释放引用
* mm_unref(MM_POOL_TENSOR, data);
*
* // 查看统计
* mm_dump(stdout);
*
* // 清理
* mm_deinit();
* @endcode
*/
#ifndef MM_H
#define MM_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/*============================================================================
* 编译时配置
*============================================================================*/
/**
* @defgroup 编译配置
* @{
*/
/** @brief 最大内存池数量(包括动态池) */
#define MM_MAX_POOLS 16
/** @brief 内存对齐字节数(口诀⑥:对齐边界,性能优) */
#define MM_ALIGNMENT 64
/** @brief 块头魔数,用于检测内存损坏 */
#define MM_MAGIC 0x4D4D4D4D
/** @brief 调试模式开关(1=启用追踪,0=禁用) */
#ifndef MM_DEBUG
#define MM_DEBUG 0
#endif
/** @} */
/*============================================================================
* 错误码定义
*============================================================================*/
/**
* @defgroup 错误码
* @{
*/
/**
* @brief 内存管理模块错误码
*/
typedef enum {
MM_OK = 0, /**< 操作成功 */
MM_ERR_INVALID_POOL = -1, /**< 无效的内存池ID */
MM_ERR_OUT_OF_MEMORY = -2, /**< 内存不足 */
MM_ERR_INVALID_PTR = -3, /**< 无效的内存指针 */
MM_ERR_SIZE_TOO_LARGE = -4, /**< 请求大小超过槽位限制 */
MM_ERR_POOL_FULL = -5, /**< 内存池已满 */
MM_ERR_NOT_INITIALIZED = -6, /**< 模块未初始化 */
MM_ERR_CORRUPTED = -7, /**< 内存块损坏(魔数校验失败) */
} mm_error_t;
/**
* @brief 获取错误码对应的描述字符串
* @param err 错误码
* @return 错误描述字符串
*/
const char* mm_error_string(mm_error_t err);
/** @} */
/*============================================================================
* 内存池类型
*============================================================================*/
/**
* @defgroup 预定义池类型
* @{
*/
/**
* @brief 预定义内存池类型
*
* 这些池在 mm_init() 时自动创建,使用静态内存。
*/
typedef enum {
MM_POOL_TENSOR = 0, /**< 张量数据池 - 512KB, 8槽位, 每槽64KB */
MM_POOL_MODEL = 1, /**< 模型数据池 - 1MB, 4槽位, 每槽256KB */
MM_POOL_FRAME = 2, /**< 帧缓冲池 - 256KB, 4槽位, 每槽64KB */
MM_POOL_CUSTOM = 3, /**< 自定义池起始索引 */
} mm_pool_type_t;
/** @} */
/*============================================================================
* 数据结构定义
*============================================================================*/
/**
* @defgroup 数据结构
* @{
*/
/**
* @brief 内存块头部结构
*
* 每个分配的内存块前都有一个头部,用于管理。
* 布局: [mm_block_t][user_data...]
* ^ ^
* block user_ptr
*/
/* 修改 mm_block_t 结构体定义,添加64字节对齐属性 */
typedef struct mm_block {
struct mm_block* next; /**< 下一个空闲块 */
uint32_t ref_count; /**< 引用计数 */
uint32_t magic; /**< 魔数校验 */
size_t size; /**< 用户数据大小 */
int pool_id; /**< 所属池ID */
uint32_t slot_idx; /**< 槽位索引 */
uint32_t alloc_line; /**< 分配行号 */
const char* alloc_file; /**< 分配文件名 */
void* user_ptr; /**< 用户数据指针 */
} __attribute__((aligned(64))) mm_block_t;
/**
* @brief 内存池配置信息
*
* 描述一个内存池的基本属性,存储在只读数据段(口诀⑧:静态区,放常量)。
*/
typedef struct {
const char* name; /**< 池名称 */
size_t total_size; /**< 总大小(字节) */
size_t slot_size; /**< 单槽大小(字节,包含头部) */
size_t slot_count; /**< 槽位数量 */
size_t alignment; /**< 对齐边界(字节) */
int is_dynamic; /**< 是否动态池(0=静态,1=动态) */
} mm_pool_config_t;
/**
* @brief 内存池统计信息
*
* 用于查询内存池的使用情况。
*/
typedef struct {
const char* name; /**< 池名称 */
size_t slot_size; /**< 槽位大小(字节) */
size_t total_slots; /**< 总槽位数 */
size_t used_slots; /**< 已使用槽位数 */
size_t free_slots; /**< 空闲槽位数 */
size_t total_alloc_bytes; /**< 当前分配总字节数 */
size_t peak_alloc_bytes; /**< 历史峰值分配字节数 */
uint64_t alloc_count; /**< 历史分配次数 */
uint64_t free_count; /**< 历史释放次数 */
uint64_t leak_count; /**< 泄漏次数(仅检测时有效) */
} mm_stats_t;
/** @} */
/*============================================================================
* 核心API - 初始化和销毁
*============================================================================*/
/**
* @defgroup 初始化和销毁
* @{
*/
/**
* @brief 初始化内存管理模块
*
* 创建所有预定义的内存池,初始化空闲链表。
* 遵循口诀①:预分配,不动态
*
* @return 错误码
* @retval MM_OK 初始化成功
* @retval MM_ERR_OUT_OF_MEMORY 内存不足
*
* @note 必须在任何其他内存操作之前调用
*/
mm_error_t mm_init(void);
/**
* @brief 反初始化内存管理模块
*
* 释放所有动态内存池,检测并报告内存泄漏。
*
* @return 错误码
* @retval MM_OK 反初始化成功
*
* @note 调用后所有之前分配的内存指针将失效
*/
mm_error_t mm_deinit(void);
/**
* @brief 检查模块是否已初始化
* @return true=已初始化, false=未初始化
*/
bool mm_is_initialized(void);
/** @} */
/*============================================================================
* 内存池管理API
*============================================================================*/
/**
* @defgroup 内存池管理
* @{
*/
/**
* @brief 创建自定义内存池(动态池)
*
* @param name 池名称(不能重复)
* @param slot_size 槽位大小(字节)
* @param slot_count 槽位数量
* @param out_pool_id 输出池ID
* @return 错误码
* @retval MM_OK 创建成功
* @retval MM_ERR_INVALID_PTR 参数无效
* @retval MM_ERR_OUT_OF_MEMORY 内存不足
*
* @note 动态池使用 malloc 分配内存,但内部管理仍是固定槽位
*/
mm_error_t mm_pool_create(const char* name, size_t slot_size, size_t slot_count, int* out_pool_id);
/**
* @brief 销毁自定义内存池
* @param pool_id 池ID
* @return 错误码
* @retval MM_OK 销毁成功
* @retval MM_ERR_POOL_FULL 池中仍有未释放的内存
*
* @note 只能销毁动态池,不能销毁预定义池
*/
mm_error_t mm_pool_destroy(int pool_id);
/**
* @brief 获取预定义池的ID
* @param type 池类型
* @return 池ID,失败返回-1
*/
int mm_pool_id(mm_pool_type_t type);
/**
* @brief 获取池配置信息
* @param pool_id 池ID
* @param config 输出配置
* @return 错误码
*/
mm_error_t mm_pool_config(int pool_id, mm_pool_config_t* config);
/** @} */
/*============================================================================
* 内存分配/释放API
*============================================================================*/
/**
* @defgroup 分配释放
* @{
*/
/**
* @brief 从指定池分配内存
*
* 遵循口诀④:谁申请,谁释放
*
* @param pool_id 池ID
* @param size 需要的大小(字节,必须 ≤ 槽位大小)
* @return 内存指针,失败返回NULL
*
* @note 分配的内存自动清零
* @note 分配后 ref_count = 1
*/
void* mm_alloc(int pool_id, size_t size);
/**
* @brief 带调试信息的分配(通过宏调用)
*
* @param pool_id 池ID
* @param size 大小
* @param file 文件名(由宏自动填充)
* @param line 行号(由宏自动填充)
* @return 内存指针
*/
void* mm_alloc_dbg(int pool_id, size_t size, const char* file, int line);
/**
* @brief 释放内存
*
* 遵循口诀④:谁申请,谁释放
*
* @param pool_id 池ID
* @param ptr 内存指针
*/
void mm_free(int pool_id, void* ptr);
/**
* @brief 获取分配的内存块大小
* @param ptr 内存指针
* @return 实际分配大小(字节),失败返回0
*/
size_t mm_get_size(void* ptr);
/**
* @brief 获取内存块所属池ID
* @param ptr 内存指针
* @return 池ID,失败返回-1
*/
int mm_get_pool_id(void* ptr);
/** @} */
/*============================================================================
* 引用计数API
*============================================================================*/
/**
* @defgroup 引用计数
* @{
*/
/**
* @brief 增加内存块的引用计数
*
* 遵循口诀⑤:引用计数,防泄漏
*
* @param ptr 内存指针
* @return 新的引用计数,失败返回-1
*/
int mm_ref(void* ptr);
/**
* @brief 减少内存块的引用计数
*
* 当引用计数降为0时,自动释放内存。
* 遵循口诀⑤:引用计数,防泄漏
*
* @param pool_id 池ID
* @param ptr 内存指针
* @return 新的引用计数,失败返回-1
*/
int mm_unref(int pool_id, void* ptr);
/**
* @brief 获取内存块的引用计数
* @param ptr 内存指针
* @return 引用计数,失败返回-1
*/
int mm_refcount(void* ptr);
/** @} */
/*============================================================================
* 统计和调试API
*============================================================================*/
/**
* @defgroup 统计调试
* @{
*/
/**
* @brief 获取内存池统计信息
* @param pool_id 池ID
* @param stats 输出统计信息
* @return 错误码
*/
mm_error_t mm_stats(int pool_id, mm_stats_t* stats);
/**
* @brief 打印所有内存池状态
* @param out 输出流(NULL=stdout)
*/
void mm_dump(FILE* out);
/**
* @brief 检测并报告内存泄漏
* @param out 输出流(NULL=stdout)
* @return 泄漏的块数量
*/
int mm_check_leaks(FILE* out);
/**
* @brief 打印指定内存块的详细信息
* @param ptr 内存指针
* @param out 输出流(NULL=stdout)
*/
void mm_dump_block(void* ptr, FILE* out);
/**
* @brief 重置指定内存池(清空所有分配)
* @param pool_id 池ID
* @return 错误码
* @note 仅用于测试,强制释放所有内存
*/
mm_error_t mm_pool_reset(int pool_id);
/**
* @brief 重置所有内存池
* @note 仅用于测试
*/
void mm_reset_all(void);
/** @} */
/*============================================================================
* 栈内存辅助宏
*============================================================================*/
/**
* @defgroup 栈内存宏
* @{
*/
/**
* @brief 在栈上分配数组(推荐用于 < 4KB 的数据)
*
* 遵循口诀⑦:栈优先,堆次之
*
* @param type 数组元素类型
* @param name 数组变量名
* @param size 数组大小
*/
#define MM_STACK_ARRAY(type, name, size) \
type name[size]; \
memset(name, 0, sizeof(name))
/**
* @brief 在栈上分配并初始化数组
*
* 遵循口诀⑦:栈优先,堆次之
*
* @param type 数组元素类型
* @param name 数组变量名
* @param size 数组大小
* @param init_val 初始值
*/
#define MM_STACK_INIT(type, name, size, init_val) \
type name[size]; \
for (size_t _i = 0; _i < (size); _i++) name[_i] = (init_val)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* MM_H */
2. src/mm.c
/**
* @file mm.c
* @brief 内存管理模块实现
* @author Memory Manager Team
* @version 2.0.0
*
* @details 实现内存池管理,严格遵循内存管理七字诀:
* ① 预分配,不动态 ② 定大小,不分片
* ③ 池化管理,零碎片 ④ 谁申请,谁释放
* ⑤ 引用计数,防泄漏 ⑥ 对齐边界,性能优
* ⑦ 栈优先,堆次之 ⑧ 静态区,放常量
*/
#include "mm.h"
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/*============================================================================
* 调试宏 (MM_DEBUG=1 时启用追踪)
*============================================================================*/
#ifndef MM_DEBUG
#define MM_DEBUG 0
#endif
#if MM_DEBUG
#define MM_TRACE(fmt, ...) \
printf("[MM_TRACE] %s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
#define MM_ERROR(fmt, ...) \
printf("[MM_ERROR] %s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
#define MM_TRACE(fmt, ...)
#define MM_ERROR(fmt, ...)
#endif
/*============================================================================
* 静态内存池数据区 (口诀①: 预分配,不动态;口诀⑥: 对齐边界,性能优)
*============================================================================*/
/**
* @brief 张量池 - 512KB,64字节对齐
*
* 用于存储张量数据,8个槽位,每槽64KB。
* 遵循口诀①:预分配,不动态
* 遵循口诀⑥:对齐边界,性能优
*/
static uint8_t g_tensor_pool[512 * 1024] __attribute__((aligned(MM_ALIGNMENT)));
/**
* @brief 模型池 - 1MB,64字节对齐
*
* 用于存储模型数据,4个槽位,每槽256KB。
* 遵循口诀①:预分配,不动态
*/
static uint8_t g_model_pool[1024 * 1024] __attribute__((aligned(MM_ALIGNMENT)));
/**
* @brief 帧缓冲池 - 256KB,64字节对齐
*
* 用于存储帧缓冲区,4个槽位,每槽64KB。
* 遵循口诀①:预分配,不动态
*/
static uint8_t g_frame_pool[256 * 1024] __attribute__((aligned(MM_ALIGNMENT)));
/*============================================================================
* 预定义池配置表 (口诀⑧: 静态区,放常量)
*============================================================================*/
/**
* @brief 预定义内存池配置表
*
* 这些配置在编译时确定,存储在只读数据段。
* 遵循口诀⑧:静态区,放常量
*/
static const mm_pool_config_t g_default_configs[] = {
[MM_POOL_TENSOR] = {
.name = "TensorPool",
.total_size = 512 * 1024,
.slot_size = 64 * 1024,
.slot_count = 8,
.alignment = MM_ALIGNMENT,
.is_dynamic = 0,
},
[MM_POOL_MODEL] = {
.name = "ModelPool",
.total_size = 1024 * 1024,
.slot_size = 256 * 1024,
.slot_count = 4,
.alignment = MM_ALIGNMENT,
.is_dynamic = 0,
},
[MM_POOL_FRAME] = {
.name = "FramePool",
.total_size = 256 * 1024,
.slot_size = 64 * 1024,
.slot_count = 4,
.alignment = MM_ALIGNMENT,
.is_dynamic = 0,
},
};
/*============================================================================
* 动态池结构定义
*============================================================================*/
/**
* @brief 动态内存池结构
*
* 用于管理运行时创建的自定义内存池。
*/
typedef struct dynamic_pool {
char name[64]; /**< 池名称 */
mm_pool_config_t config; /**< 池配置 */
uint8_t* base_ptr; /**< 池基地址(malloc分配) */
mm_block_t* free_list; /**< 空闲块链表头 */
uint32_t* slot_used; /**< 槽位使用标志数组 */
size_t used_count; /**< 已使用槽位数 */
size_t free_count; /**< 空闲槽位数 */
uint64_t alloc_count; /**< 历史分配次数 */
uint64_t free_count_total; /**< 历史释放次数 */
size_t peak_alloc_bytes; /**< 峰值分配字节数 */
pthread_mutex_t mutex; /**< 池互斥锁 */
int initialized; /**< 初始化标志 */
} dynamic_pool_t;
/*============================================================================
* 全局状态结构
*============================================================================*/
/**
* @brief 全局内存管理状态
*
* 管理所有内存池和全局统计信息。
*/
static struct {
int initialized; /**< 初始化标志 */
pthread_mutex_t global_mutex; /**< 全局互斥锁 */
/* 静态池数据 */
uint8_t* static_pools[MM_POOL_CUSTOM]; /**< 静态池基地址 */
mm_block_t* static_free_lists[MM_POOL_CUSTOM]; /**< 静态池空闲链表 */
uint32_t static_slot_used[MM_POOL_CUSTOM][32]; /**< 静态池槽位使用标志 */
size_t static_used_count[MM_POOL_CUSTOM]; /**< 静态池已用槽位数 */
size_t static_free_count[MM_POOL_CUSTOM]; /**< 静态池空闲槽位数 */
int static_initialized[MM_POOL_CUSTOM]; /**< 静态池初始化标志 */
/* 动态池数据 */
dynamic_pool_t dynamic_pools[MM_MAX_POOLS - MM_POOL_CUSTOM]; /**< 动态池数组 */
int dynamic_pool_count; /**< 动态池数量 */
/* 全局统计 */
uint64_t total_alloc_count; /**< 总分配次数 */
uint64_t total_free_count; /**< 总释放次数 */
size_t total_alloc_bytes; /**< 总分配字节数 */
uint64_t total_leak_count; /**< 总泄漏块数 */
} g_mm = {
.initialized = 0,
.global_mutex = PTHREAD_MUTEX_INITIALIZER,
.static_pools = {NULL, NULL, NULL},
.static_initialized = {0, 0, 0},
.dynamic_pool_count = 0,
.total_alloc_count = 0,
.total_free_count = 0,
.total_alloc_bytes = 0,
.total_leak_count = 0,
};
/*============================================================================
* 内部辅助函数
*============================================================================*/
/**
* @brief 从用户指针获取块头部
* @param ptr 用户数据指针
* @return 块头部指针,失败返回NULL
*/
static mm_block_t* block_from_ptr(void* ptr) {
if (!ptr) return NULL;
/* 由于 mm_block_t 是64字节对齐,用户指针紧跟其后,偏移量固定 */
return (mm_block_t*)((uint8_t*)ptr - sizeof(mm_block_t));
}
/**
* @brief 动态池释放函数
* @param pool 动态池指针
* @param ptr 要释放的内存指针
*
* @note 增加双重释放保护,检查槽位是否已被释放
* @note 此函数在 mm_free 中被调用
*/
static void dynamic_pool_free(dynamic_pool_t* pool, void* ptr) {
mm_block_t* block = block_from_ptr(ptr);
if (!block || block->magic != MM_MAGIC) {
MM_ERROR("Invalid block at %p", ptr);
return;
}
/* 验证 slot_idx 有效 */
if (block->slot_idx >= pool->config.slot_count) {
MM_ERROR("Invalid slot_idx %u (max %zu)", block->slot_idx, pool->config.slot_count);
return;
}
pthread_mutex_lock(&pool->mutex);
/* 关键修复:检查是否已经被释放,防止双重释放 */
if (pool->slot_used[block->slot_idx] == 0) {
MM_TRACE("Double free detected on slot %u", block->slot_idx);
pthread_mutex_unlock(&pool->mutex);
return;
}
block->ref_count = 0;
pool->slot_used[block->slot_idx] = 0;
pool->used_count--;
pool->free_count++;
pool->free_count_total++;
block->next = pool->free_list;
pool->free_list = block;
MM_TRACE("Freed slot %u, used_count=%zu", block->slot_idx, pool->used_count);
pthread_mutex_unlock(&pool->mutex);
}
/**
* @brief 验证块头部完整性
* @param block 块头部指针
* @return 1=有效, 0=无效
*/
static int block_valid(mm_block_t* block) {
uintptr_t addr;
if (!block) return 0;
addr = (uintptr_t)block;
/* 检查是否在静态池范围内 */
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
if (g_mm.static_initialized[i] && g_mm.static_pools[i]) {
uintptr_t base = (uintptr_t)g_mm.static_pools[i];
uintptr_t end = base + g_default_configs[i].total_size;
if (addr >= base && addr < end) {
return (block->magic == MM_MAGIC);
}
}
}
/* 检查是否在动态池范围内 */
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[i];
if (pool->initialized && pool->base_ptr) {
uintptr_t base = (uintptr_t)pool->base_ptr;
uintptr_t end = base + pool->config.total_size;
if (addr >= base && addr < end) {
return (block->magic == MM_MAGIC);
}
}
}
return 0;
}
/**
* @brief 检查指针是否属于指定静态池
* @param pool_id 池ID
* @param ptr 内存指针
* @return 1=属于, 0=不属于
*/
/*static int ptr_in_static_pool(int pool_id, void* ptr) {
uintptr_t addr, base, end;
if (!ptr) return 0;
if (pool_id < 0 || pool_id >= MM_POOL_CUSTOM) return 0;
if (!g_mm.static_pools[pool_id]) return 0;
addr = (uintptr_t)ptr;
base = (uintptr_t)g_mm.static_pools[pool_id];
end = base + g_default_configs[pool_id].total_size;
return (addr >= base && addr < end);
}*/
/*============================================================================
* 静态池初始化函数
*============================================================================*/
/**
* @brief 初始化静态内存池
*
* 由于 mm_block_t 已强制64字节对齐,sizeof(mm_block_t) 是64的倍数,
* 所以 user_ptr 自然对齐到64字节边界。
*
* @param pool_id 池ID (0-2)
* @param config 池配置
* @param base_ptr 池基地址
*/
static void init_static_pool(int pool_id, const mm_pool_config_t* config, uint8_t* base_ptr) {
size_t slot_size = config->slot_size;
size_t slot_count = config->slot_count;
mm_block_t* prev = NULL;
MM_TRACE("Initializing pool %s: base=%p, size=%zu, slots=%zu, slot_size=%zu",
config->name, base_ptr, config->total_size, slot_count, slot_size);
g_mm.static_pools[pool_id] = base_ptr;
g_mm.static_used_count[pool_id] = 0;
g_mm.static_free_count[pool_id] = slot_count;
g_mm.static_initialized[pool_id] = 1;
memset(g_mm.static_slot_used[pool_id], 0, sizeof(g_mm.static_slot_used[pool_id]));
for (size_t i = 0; i < slot_count; i++) {
uint8_t* slot_start = base_ptr + i * slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
/* 由于 mm_block_t 已64字节对齐,用户指针自然对齐 */
block->next = NULL;
block->ref_count = 0;
block->magic = MM_MAGIC;
block->size = slot_size - sizeof(mm_block_t);
block->pool_id = pool_id;
block->slot_idx = i;
block->alloc_line = 0;
block->alloc_file = NULL;
block->user_ptr = (uint8_t*)slot_start + sizeof(mm_block_t);
if (prev) {
prev->next = block;
} else {
g_mm.static_free_lists[pool_id] = block;
}
prev = block;
MM_TRACE(" Slot %zu: user_ptr=%p, size=%zu", i, block->user_ptr, block->size);
}
MM_TRACE("Pool %s initialized, free_list=%p", config->name, g_mm.static_free_lists[pool_id]);
}
/*============================================================================
* 静态池分配释放函数
*============================================================================*/
/**
* @brief 从静态池分配内存
* @param pool_id 池ID
* @param size 需要的大小
* @param file 分配文件名(调试用)
* @param line 分配行号(调试用)
* @return 内存指针,失败返回NULL
*/
static void* static_pool_alloc(int pool_id, size_t size, const char* file, int line) {
mm_block_t* block;
void* result;
MM_TRACE("Alloc from pool %d: size=%zu, file=%s:%d", pool_id, size, file ? file : "?", line);
if (!g_mm.static_initialized[pool_id]) {
MM_ERROR("Pool %d not initialized!", pool_id);
return NULL;
}
block = g_mm.static_free_lists[pool_id];
if (!block) {
MM_ERROR("No free block in pool %d", pool_id);
return NULL;
}
/* 检查大小是否超过槽位容量 */
if (size > block->size) {
MM_ERROR("Size %zu > block capacity %zu", size, block->size);
return NULL; /* 关键修复:返回NULL,不分配 */
}
MM_TRACE(" Got block at %p, slot_idx=%u, user_ptr=%p, capacity=%zu",
block, block->slot_idx, block->user_ptr, block->size);
/* 从链表中移除 */
g_mm.static_free_lists[pool_id] = block->next;
/* 标记槽位已使用 */
g_mm.static_slot_used[pool_id][block->slot_idx] = 1;
g_mm.static_used_count[pool_id]++;
g_mm.static_free_count[pool_id]--;
/* 保存请求的大小 */
block->size = size;
block->ref_count = 1;
block->magic = MM_MAGIC;
block->pool_id = pool_id;
block->alloc_line = line;
block->alloc_file = file;
result = block->user_ptr;
memset(result, 0, size);
MM_TRACE(" Allocated %p, size=%zu", result, size);
g_mm.total_alloc_count++;
g_mm.total_alloc_bytes += size;
return result;
}
/**
* @brief 释放内存回静态池
* @param pool_id 池ID
* @param ptr 内存指针
*/
/*static void static_pool_free(int pool_id, void* ptr) {
mm_block_t* block;
size_t slot_size = g_default_configs[pool_id].slot_size;
MM_TRACE("Free from pool %d: ptr=%p", pool_id, ptr);
if (!ptr_in_static_pool(pool_id, ptr)) {
MM_ERROR("Pointer %p not in pool %d", ptr, pool_id);
return;
}
block = block_from_ptr(ptr);
if (!block_valid(block)) {
MM_ERROR("Invalid block at %p", ptr);
return;
}
MM_TRACE(" Block at %p, slot_idx=%u, size=%zu, ref=%u",
block, block->slot_idx, block->size, block->ref_count);
block->ref_count = 0;
block->alloc_line = 0;
block->alloc_file = NULL;
// 关键修复:恢复块容量为槽位容量
block->size = slot_size - sizeof(mm_block_t);
g_mm.static_slot_used[pool_id][block->slot_idx] = 0;
g_mm.static_used_count[pool_id]--;
g_mm.static_free_count[pool_id]++;
block->next = g_mm.static_free_lists[pool_id];
g_mm.static_free_lists[pool_id] = block;
g_mm.total_free_count++;
MM_TRACE(" Freed, size restored to %zu", block->size);
}*/
/*============================================================================
* 动态池管理函数
*============================================================================*/
/**
* @brief 根据名称查找动态池
* @param name 池名称
* @return 池ID,失败返回-1
*/
static int find_dynamic_pool(const char* name) {
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
if (strcmp(g_mm.dynamic_pools[i].name, name) == 0) {
return MM_POOL_CUSTOM + i;
}
}
return -1;
}
/**
* @brief 创建动态内存池
* @param name 池名称
* @param slot_size 槽位大小
* @param slot_count 槽位数量
* @param out_pool_id 输出池ID
* @return 错误码
*/
static mm_error_t dynamic_pool_create(const char* name, size_t slot_size, size_t slot_count, int* out_pool_id) {
dynamic_pool_t* pool;
size_t total_size;
uint8_t* base_ptr;
mm_block_t* prev = NULL;
int pool_id;
/* 检查是否已存在同名池 */
pool_id = find_dynamic_pool(name);
if (pool_id >= 0) {
if (out_pool_id) *out_pool_id = pool_id;
return MM_OK;
}
/* 检查池数量是否超限 */
if (g_mm.dynamic_pool_count >= MM_MAX_POOLS - MM_POOL_CUSTOM) {
return MM_ERR_OUT_OF_MEMORY;
}
/* 对齐槽位大小(口诀⑥:对齐边界,性能优) */
slot_size = (slot_size + MM_ALIGNMENT - 1) & ~(MM_ALIGNMENT - 1);
total_size = (slot_size + sizeof(mm_block_t)) * slot_count;
/* 分配内存(动态池允许malloc,但内部管理仍是固定槽位) */
base_ptr = (uint8_t*)aligned_alloc(MM_ALIGNMENT, total_size);
if (!base_ptr) {
return MM_ERR_OUT_OF_MEMORY;
}
/* 初始化动态池结构 */
pool = &g_mm.dynamic_pools[g_mm.dynamic_pool_count];
memset(pool, 0, sizeof(dynamic_pool_t));
strncpy(pool->name, name, sizeof(pool->name) - 1);
pool->config.name = pool->name;
pool->config.total_size = total_size;
pool->config.slot_size = slot_size + sizeof(mm_block_t);
pool->config.slot_count = slot_count;
pool->config.alignment = MM_ALIGNMENT;
pool->config.is_dynamic = 1;
pool->base_ptr = base_ptr;
pool->used_count = 0;
pool->free_count = slot_count;
pool->alloc_count = 0;
pool->free_count_total = 0;
pool->peak_alloc_bytes = 0;
pthread_mutex_init(&pool->mutex, NULL);
pool->initialized = 1;
/* 分配槽位使用标志数组 */
pool->slot_used = (uint32_t*)calloc(slot_count, sizeof(uint32_t));
if (!pool->slot_used) {
free(base_ptr);
return MM_ERR_OUT_OF_MEMORY;
}
/* 构建空闲链表(口诀③:池化管理,零碎片) */
for (size_t i = 0; i < slot_count; i++) {
uint8_t* slot_start = base_ptr + i * (slot_size + sizeof(mm_block_t));
mm_block_t* block = (mm_block_t*)slot_start;
block->next = NULL;
block->ref_count = 0;
block->magic = MM_MAGIC;
block->size = slot_size;
block->pool_id = MM_POOL_CUSTOM + g_mm.dynamic_pool_count;
block->slot_idx = i;
block->alloc_line = 0;
block->alloc_file = NULL;
block->user_ptr = slot_start + sizeof(mm_block_t);
if (prev) {
prev->next = block;
} else {
pool->free_list = block;
}
prev = block;
}
*out_pool_id = MM_POOL_CUSTOM + g_mm.dynamic_pool_count;
g_mm.dynamic_pool_count++;
return MM_OK;
}
/*============================================================================
* 公共API实现 - 错误处理
*============================================================================*/
/**
* @brief 获取错误码描述字符串
* @param err 错误码
* @return 错误描述字符串
*/
const char* mm_error_string(mm_error_t err) {
switch (err) {
case MM_OK: return "Success";
case MM_ERR_INVALID_POOL: return "Invalid pool";
case MM_ERR_OUT_OF_MEMORY: return "Out of memory";
case MM_ERR_INVALID_PTR: return "Invalid pointer";
case MM_ERR_SIZE_TOO_LARGE: return "Size too large for slot";
case MM_ERR_POOL_FULL: return "Pool is full";
case MM_ERR_NOT_INITIALIZED: return "Not initialized";
case MM_ERR_CORRUPTED: return "Memory corrupted";
default: return "Unknown error";
}
}
/*============================================================================
* 公共API实现 - 初始化和销毁
*============================================================================*/
/**
* @brief 初始化内存管理模块
* @return 错误码
*/
mm_error_t mm_init(void) {
pthread_mutex_lock(&g_mm.global_mutex);
MM_TRACE("mm_init called, initialized=%d", g_mm.initialized);
if (g_mm.initialized) {
MM_TRACE("Already initialized");
pthread_mutex_unlock(&g_mm.global_mutex);
return MM_OK;
}
/* 初始化三个静态池 */
MM_TRACE("Initializing TensorPool...");
init_static_pool(MM_POOL_TENSOR, &g_default_configs[0], g_tensor_pool);
MM_TRACE("Initializing ModelPool...");
init_static_pool(MM_POOL_MODEL, &g_default_configs[1], g_model_pool);
MM_TRACE("Initializing FramePool...");
init_static_pool(MM_POOL_FRAME, &g_default_configs[2], g_frame_pool);
g_mm.initialized = 1;
MM_TRACE("mm_init completed successfully");
pthread_mutex_unlock(&g_mm.global_mutex);
return MM_OK;
}
/**
* @brief 反初始化内存管理模块
* @return 错误码
*/
mm_error_t mm_deinit(void) {
pthread_mutex_lock(&g_mm.global_mutex);
if (!g_mm.initialized) {
pthread_mutex_unlock(&g_mm.global_mutex);
return MM_OK;
}
/* 检测泄漏 */
mm_check_leaks(stdout);
/* 重置静态池状态 */
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
g_mm.static_pools[i] = NULL;
g_mm.static_free_lists[i] = NULL;
g_mm.static_used_count[i] = 0;
g_mm.static_free_count[i] = 0;
g_mm.static_initialized[i] = 0;
memset(g_mm.static_slot_used[i], 0, sizeof(g_mm.static_slot_used[i]));
}
/* 释放动态池 */
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[i];
if (pool->initialized) {
if (pool->base_ptr) free(pool->base_ptr);
if (pool->slot_used) free(pool->slot_used);
pthread_mutex_destroy(&pool->mutex);
pool->initialized = 0;
}
}
g_mm.dynamic_pool_count = 0;
g_mm.initialized = 0;
pthread_mutex_unlock(&g_mm.global_mutex);
return MM_OK;
}
/**
* @brief 检查是否已初始化
* @return true=已初始化, false=未初始化
*/
bool mm_is_initialized(void) {
return g_mm.initialized;
}
/*============================================================================
* 公共API实现 - 内存池管理
*============================================================================*/
/**
* @brief 创建自定义内存池
* @param name 池名称
* @param slot_size 槽位大小
* @param slot_count 槽位数量
* @param out_pool_id 输出池ID
* @return 错误码
*/
mm_error_t mm_pool_create(const char* name, size_t slot_size, size_t slot_count, int* out_pool_id) {
mm_error_t err;
if (!name || !out_pool_id) return MM_ERR_INVALID_PTR;
if (slot_size == 0 || slot_count == 0) return MM_ERR_INVALID_PTR;
if (!g_mm.initialized) return MM_ERR_NOT_INITIALIZED;
pthread_mutex_lock(&g_mm.global_mutex);
err = dynamic_pool_create(name, slot_size, slot_count, out_pool_id);
pthread_mutex_unlock(&g_mm.global_mutex);
return err;
}
/**
* @brief 销毁自定义内存池
* @param pool_id 池ID
* @return 错误码
*
* @note 直接遍历 slot_used 数组检查泄漏,不依赖 used_count 计数器
*/
mm_error_t mm_pool_destroy(int pool_id) {
dynamic_pool_t* pool;
int idx;
int has_leak = 0;
/* 参数验证 */
if (pool_id < MM_POOL_CUSTOM || pool_id >= MM_MAX_POOLS) {
return MM_ERR_INVALID_POOL;
}
idx = pool_id - MM_POOL_CUSTOM;
if (idx >= g_mm.dynamic_pool_count) {
return MM_ERR_INVALID_POOL;
}
pool = &g_mm.dynamic_pools[idx];
/* 关键修复:直接遍历 slot_used 数组检查泄漏,不依赖 used_count */
for (size_t i = 0; i < pool->config.slot_count; i++) {
if (pool->slot_used[i] != 0) {
has_leak = 1;
MM_ERROR("Pool %s: slot %zu still used (size=%u)",
pool->name, i, pool->slot_used[i]);
}
}
if (has_leak) {
return MM_ERR_POOL_FULL;
}
/* 销毁资源 */
if (pool->base_ptr) {
free(pool->base_ptr);
pool->base_ptr = NULL;
}
if (pool->slot_used) {
free(pool->slot_used);
pool->slot_used = NULL;
}
pthread_mutex_destroy(&pool->mutex);
/* 移动后面的池向前,更新被移动池中所有块的 pool_id */
for (int i = idx; i < g_mm.dynamic_pool_count - 1; i++) {
g_mm.dynamic_pools[i] = g_mm.dynamic_pools[i + 1];
/* 更新被移动池中所有块的 pool_id */
dynamic_pool_t* moved_pool = &g_mm.dynamic_pools[i];
if (moved_pool->initialized && moved_pool->base_ptr) {
for (size_t j = 0; j < moved_pool->config.slot_count; j++) {
uint8_t* slot_start = moved_pool->base_ptr + j * moved_pool->config.slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
block->pool_id = MM_POOL_CUSTOM + i;
}
}
}
g_mm.dynamic_pool_count--;
return MM_OK;
}
/**
* @brief 获取预定义池ID
* @param type 池类型
* @return 池ID,失败返回-1
*/
int mm_pool_id(mm_pool_type_t type) {
if (type >= MM_POOL_CUSTOM) return -1;
return (int)type;
}
/**
* @brief 获取池配置
* @param pool_id 池ID
* @param config 输出配置
* @return 错误码
*/
mm_error_t mm_pool_config(int pool_id, mm_pool_config_t* config) {
if (!config) return MM_ERR_INVALID_PTR;
/* 静态池 */
if (pool_id >= 0 && pool_id < MM_POOL_CUSTOM) {
memcpy(config, &g_default_configs[pool_id], sizeof(mm_pool_config_t));
return MM_OK;
}
/* 动态池 */
if (pool_id >= MM_POOL_CUSTOM && pool_id < MM_MAX_POOLS) {
int idx = pool_id - MM_POOL_CUSTOM;
if (idx < g_mm.dynamic_pool_count) {
memcpy(config, &g_mm.dynamic_pools[idx].config, sizeof(mm_pool_config_t));
return MM_OK;
}
}
return MM_ERR_INVALID_POOL;
}
/*============================================================================
* 公共API实现 - 分配释放
*============================================================================*/
/**
* @brief 分配内存
* @param pool_id 池ID
* @param size 需要的大小
* @return 内存指针,失败返回NULL
*/
void* mm_alloc(int pool_id, size_t size) {
return mm_alloc_dbg(pool_id, size, NULL, 0);
}
/**
* @brief 带调试信息的分配
* @param pool_id 池ID
* @param size 大小
* @param file 文件名
* @param line 行号
* @return 内存指针,失败返回NULL
*/
void* mm_alloc_dbg(int pool_id, size_t size, const char* file, int line) {
void* ptr = NULL;
if (size == 0) return NULL;
if (!g_mm.initialized) return NULL;
/* 静态池分配 */
if (pool_id >= 0 && pool_id < MM_POOL_CUSTOM) {
if (!g_mm.static_initialized[pool_id]) {
MM_ERROR("Static pool %d not initialized", pool_id);
return NULL;
}
pthread_mutex_lock(&g_mm.global_mutex);
ptr = static_pool_alloc(pool_id, size, file, line);
pthread_mutex_unlock(&g_mm.global_mutex);
/* 关键修复:如果分配失败,直接返回NULL */
return ptr;
}
/* 动态池分配 */
if (pool_id >= MM_POOL_CUSTOM && pool_id < MM_MAX_POOLS) {
int idx = pool_id - MM_POOL_CUSTOM;
if (idx < g_mm.dynamic_pool_count) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[idx];
mm_block_t* block;
pthread_mutex_lock(&pool->mutex);
block = pool->free_list;
if (!block) {
pthread_mutex_unlock(&pool->mutex);
return NULL; /* 修复:返回NULL而不是继续 */
}
pool->free_list = block->next;
pool->slot_used[block->slot_idx] = 1;
pool->used_count++;
pool->free_count--;
pool->alloc_count++;
size_t total_bytes = pool->used_count * pool->config.slot_size;
if (total_bytes > pool->peak_alloc_bytes) {
pool->peak_alloc_bytes = total_bytes;
}
block->ref_count = 1;
block->magic = MM_MAGIC;
block->size = size;
block->pool_id = pool_id;
block->alloc_line = line;
block->alloc_file = file;
ptr = block->user_ptr;
memset(ptr, 0, size);
g_mm.total_alloc_count++;
g_mm.total_alloc_bytes += size;
pthread_mutex_unlock(&pool->mutex);
return ptr;
}
}
return NULL;
}
/**
* @brief 释放内存
* @param pool_id 池ID
* @param ptr 内存指针
*/
void mm_free(int pool_id, void* ptr) {
mm_block_t* block;
if (!ptr) return;
if (!g_mm.initialized) return;
/* 静态池释放 */
if (pool_id >= 0 && pool_id < MM_POOL_CUSTOM) {
if (!g_mm.static_initialized[pool_id]) return;
if (!g_mm.static_pools[pool_id]) return;
uintptr_t addr = (uintptr_t)ptr;
uintptr_t base = (uintptr_t)g_mm.static_pools[pool_id];
uintptr_t end = base + g_default_configs[pool_id].total_size;
if (addr < base || addr >= end) {
MM_TRACE("Pointer %p not in pool %d range", ptr, pool_id);
return;
}
block = block_from_ptr(ptr);
if (!block_valid(block)) {
MM_ERROR("Invalid block at %p", ptr);
return;
}
if (block->pool_id != pool_id) {
MM_ERROR("Block pool_id %d != %d", block->pool_id, pool_id);
return;
}
pthread_mutex_lock(&g_mm.global_mutex);
block->ref_count = 0;
block->alloc_line = 0;
block->alloc_file = NULL;
g_mm.static_slot_used[pool_id][block->slot_idx] = 0;
g_mm.static_used_count[pool_id]--;
g_mm.static_free_count[pool_id]++;
/* 恢复块容量 */
block->size = g_default_configs[pool_id].slot_size - sizeof(mm_block_t);
block->next = g_mm.static_free_lists[pool_id];
g_mm.static_free_lists[pool_id] = block;
g_mm.total_free_count++;
pthread_mutex_unlock(&g_mm.global_mutex);
return;
}
/* 动态池释放 - 调用 dynamic_pool_free */
if (pool_id >= MM_POOL_CUSTOM && pool_id < MM_MAX_POOLS) {
int idx = pool_id - MM_POOL_CUSTOM;
if (idx < g_mm.dynamic_pool_count) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[idx];
uintptr_t addr = (uintptr_t)ptr;
uintptr_t base = (uintptr_t)pool->base_ptr;
uintptr_t end = base + pool->config.total_size;
if (addr < base || addr >= end) {
MM_TRACE("Pointer %p not in dynamic pool %d", ptr, pool_id);
return;
}
/* 调用 dynamic_pool_free 函数 */
dynamic_pool_free(pool, ptr);
g_mm.total_free_count++;
return;
}
}
}
/**
* @brief 获取内存块大小
* @param ptr 内存指针
* @return 实际分配大小,失败返回0
*/
size_t mm_get_size(void* ptr) {
mm_block_t* block;
uintptr_t addr;
if (!ptr) return 0;
addr = (uintptr_t)ptr;
/* 先检查指针是否在有效内存池范围内 */
int valid = 0;
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
if (g_mm.static_initialized[i] && g_mm.static_pools[i]) {
uintptr_t base = (uintptr_t)g_mm.static_pools[i];
uintptr_t end = base + g_default_configs[i].total_size;
if (addr >= base && addr < end) {
valid = 1;
break;
}
}
}
if (!valid) {
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[i];
if (pool->initialized && pool->base_ptr) {
uintptr_t base = (uintptr_t)pool->base_ptr;
uintptr_t end = base + pool->config.total_size;
if (addr >= base && addr < end) {
valid = 1;
break;
}
}
}
}
if (!valid) {
return 0;
}
block = block_from_ptr(ptr);
if (!block_valid(block)) {
return 0;
}
return block->size;
}
/**
* @brief 获取内存块所属池ID
* @param ptr 内存指针
* @return 池ID,失败返回-1
*/
int mm_get_pool_id(void* ptr) {
mm_block_t* block;
uintptr_t addr;
if (!ptr) return -1;
addr = (uintptr_t)ptr;
/* 先检查指针是否在有效内存池范围内 */
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
if (g_mm.static_initialized[i] && g_mm.static_pools[i]) {
uintptr_t base = (uintptr_t)g_mm.static_pools[i];
uintptr_t end = base + g_default_configs[i].total_size;
if (addr >= base && addr < end) {
block = block_from_ptr(ptr);
if (block_valid(block)) {
return block->pool_id;
}
return -1;
}
}
}
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[i];
if (pool->initialized && pool->base_ptr) {
uintptr_t base = (uintptr_t)pool->base_ptr;
uintptr_t end = base + pool->config.total_size;
if (addr >= base && addr < end) {
block = block_from_ptr(ptr);
if (block_valid(block)) {
return block->pool_id;
}
return -1;
}
}
}
return -1;
}
/*============================================================================
* 公共API实现 - 引用计数
*============================================================================*/
/**
* @brief 增加引用计数
* @param ptr 内存指针
* @return 新的引用计数,失败返回-1
*/
int mm_ref(void* ptr) {
mm_block_t* block = block_from_ptr(ptr);
if (!block_valid(block)) return -1;
return __sync_add_and_fetch(&block->ref_count, 1);
}
/**
* @brief 减少引用计数
* @param pool_id 池ID
* @param ptr 内存指针
* @return 新的引用计数,失败返回-1
*/
int mm_unref(int pool_id, void* ptr) {
mm_block_t* block = block_from_ptr(ptr);
int ref;
if (!block_valid(block)) return -1;
ref = __sync_sub_and_fetch(&block->ref_count, 1);
if (ref == 0) {
mm_free(pool_id, ptr);
}
return ref;
}
/**
* @brief 获取引用计数
* @param ptr 内存指针
* @return 引用计数,失败返回-1
*/
int mm_refcount(void* ptr) {
mm_block_t* block = block_from_ptr(ptr);
if (!block_valid(block)) return -1;
return (int)block->ref_count;
}
/*============================================================================
* 公共API实现 - 统计调试
*============================================================================*/
/**
* @brief 获取统计信息
* @param pool_id 池ID
* @param stats 输出统计信息
* @return 错误码
*/
mm_error_t mm_stats(int pool_id, mm_stats_t* stats) {
if (!stats) return MM_ERR_INVALID_PTR;
memset(stats, 0, sizeof(mm_stats_t));
/* 静态池 */
if (pool_id >= 0 && pool_id < MM_POOL_CUSTOM) {
if (!g_mm.static_initialized[pool_id]) {
return MM_ERR_NOT_INITIALIZED;
}
stats->name = g_default_configs[pool_id].name;
stats->slot_size = g_default_configs[pool_id].slot_size;
stats->total_slots = g_default_configs[pool_id].slot_count;
stats->used_slots = g_mm.static_used_count[pool_id];
stats->free_slots = g_mm.static_free_count[pool_id];
stats->total_alloc_bytes = stats->used_slots * stats->slot_size;
stats->peak_alloc_bytes = stats->total_alloc_bytes;
return MM_OK;
}
/* 动态池 */
if (pool_id >= MM_POOL_CUSTOM && pool_id < MM_MAX_POOLS) {
int idx = pool_id - MM_POOL_CUSTOM;
if (idx < g_mm.dynamic_pool_count) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[idx];
/* 加锁读取,保证一致性 */
pthread_mutex_lock(&pool->mutex);
stats->name = pool->name;
stats->slot_size = pool->config.slot_size;
stats->total_slots = pool->config.slot_count;
stats->used_slots = pool->used_count;
stats->free_slots = pool->free_count;
stats->total_alloc_bytes = pool->used_count * pool->config.slot_size;
stats->peak_alloc_bytes = pool->peak_alloc_bytes;
stats->alloc_count = pool->alloc_count;
stats->free_count = pool->free_count_total;
pthread_mutex_unlock(&pool->mutex);
return MM_OK;
}
}
return MM_ERR_INVALID_POOL;
}
/**
* @brief 打印所有池状态
* @param out 输出流
*/
void mm_dump(FILE* out) {
mm_stats_t stats;
if (!out) out = stdout;
fprintf(out, "\n");
fprintf(out, "╔════════════════════════════════════════════════════════════════════════════════╗\n");
fprintf(out, "║ Memory Pool Status ║\n");
fprintf(out, "╠════════════════════════════════════════════════════════════════════════════════╣\n");
/* 静态池 */
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
if (mm_stats(i, &stats) == MM_OK) {
fprintf(out, "║ %-12s: total=%4zu KB, slots=%2zu, used=%2zu, free=%2zu, peak=%6zu KB ║\n",
stats.name,
stats.total_slots * stats.slot_size / 1024,
stats.total_slots,
stats.used_slots,
stats.free_slots,
stats.peak_alloc_bytes / 1024);
} else {
fprintf(out, "║ %-12s: NOT INITIALIZED ║\n",
g_default_configs[i].name);
}
}
/* 动态池 */
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
int pid = MM_POOL_CUSTOM + i;
if (mm_stats(pid, &stats) == MM_OK) {
fprintf(out, "║ %-12s: total=%4zu KB, slots=%2zu, used=%2zu, free=%2zu, peak=%6zu KB ║\n",
stats.name,
stats.total_slots * stats.slot_size / 1024,
stats.total_slots,
stats.used_slots,
stats.free_slots,
stats.peak_alloc_bytes / 1024);
}
}
fprintf(out, "╠════════════════════════════════════════════════════════════════════════════════╣\n");
fprintf(out, "║ Global: allocs=%llu, frees=%llu, bytes=%llu, leaks=%llu ║\n",
(unsigned long long)g_mm.total_alloc_count,
(unsigned long long)g_mm.total_free_count,
(unsigned long long)g_mm.total_alloc_bytes,
(unsigned long long)g_mm.total_leak_count);
fprintf(out, "╚════════════════════════════════════════════════════════════════════════════════╝\n");
fprintf(out, "\n");
}
/**
* @brief 检测内存泄漏
* @param out 输出流
* @return 泄漏数量
*/
int mm_check_leaks(FILE* out) {
int leak_count = 0;
if (!out) out = stdout;
/* 检查静态池 */
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
if (g_mm.static_initialized[i] && g_mm.static_used_count[i] > 0) {
leak_count += g_mm.static_used_count[i];
fprintf(out, "[LEAK] Pool %s: %zu slots still allocated\n",
g_default_configs[i].name,
g_mm.static_used_count[i]);
for (size_t slot = 0; slot < g_default_configs[i].slot_count; slot++) {
if (g_mm.static_slot_used[i][slot]) {
uint8_t* slot_start = g_mm.static_pools[i] + slot * g_default_configs[i].slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
fprintf(out, " - Slot %zu: size=%zu, ref=%u",
slot, block->size, block->ref_count);
if (block->alloc_file) {
fprintf(out, ", at %s:%d", block->alloc_file, block->alloc_line);
}
fprintf(out, "\n");
}
}
}
}
/* 检查动态池 */
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[i];
if (pool->initialized && pool->used_count > 0) {
leak_count += pool->used_count;
fprintf(out, "[LEAK] Pool %s: %zu slots still allocated\n",
pool->name, pool->used_count);
for (size_t slot = 0; slot < pool->config.slot_count; slot++) {
if (pool->slot_used[slot]) {
uint8_t* slot_start = pool->base_ptr + slot * pool->config.slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
fprintf(out, " - Slot %zu: size=%zu, ref=%u",
slot, block->size, block->ref_count);
if (block->alloc_file) {
fprintf(out, ", at %s:%d", block->alloc_file, block->alloc_line);
}
fprintf(out, "\n");
}
}
}
}
g_mm.total_leak_count = leak_count;
return leak_count;
}
/**
* @brief 打印内存块信息
* @param ptr 内存指针
* @param out 输出流
*/
void mm_dump_block(void* ptr, FILE* out) {
mm_block_t* block;
if (!out) out = stdout;
if (!ptr) {
fprintf(out, "mm_dump_block: NULL pointer\n");
return;
}
block = block_from_ptr(ptr);
if (!block_valid(block)) {
fprintf(out, "mm_dump_block: Invalid block at %p\n", ptr);
return;
}
fprintf(out, "Block at %p:\n", ptr);
fprintf(out, " pool_id: %d\n", block->pool_id);
fprintf(out, " slot_idx: %u\n", block->slot_idx);
fprintf(out, " size: %zu bytes\n", block->size);
fprintf(out, " ref_count: %u\n", block->ref_count);
fprintf(out, " magic: 0x%08X %s\n", block->magic,
block->magic == MM_MAGIC ? "(OK)" : "(CORRUPTED)");
if (block->alloc_file) {
fprintf(out, " allocated: %s:%d\n", block->alloc_file, block->alloc_line);
}
}
/**
* @brief 重置静态池
* @param pool_id 池ID
*/
/*static void static_pool_reset(int pool_id) {
size_t slot_size = g_default_configs[pool_id].slot_size;
size_t slot_count = g_default_configs[pool_id].slot_count;
mm_block_t* prev = NULL;
uint8_t* base_ptr = g_mm.static_pools[pool_id];
if (!base_ptr) return;
g_mm.static_used_count[pool_id] = 0;
g_mm.static_free_count[pool_id] = slot_count;
memset(g_mm.static_slot_used[pool_id], 0, sizeof(g_mm.static_slot_used[pool_id]));
for (size_t i = 0; i < slot_count; i++) {
uint8_t* slot_start = base_ptr + i * slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
block->next = NULL;
block->ref_count = 0;
block->magic = MM_MAGIC;
block->size = slot_size - sizeof(mm_block_t);
block->pool_id = pool_id;
block->slot_idx = i;
block->alloc_line = 0;
block->alloc_file = NULL;
block->user_ptr = (uint8_t*)slot_start + sizeof(mm_block_t);
if (prev) {
prev->next = block;
} else {
g_mm.static_free_lists[pool_id] = block;
}
prev = block;
}
}*/
/**
* @brief 重置所有内存池
*/
void mm_reset_all(void) {
if (!g_mm.initialized) return;
MM_TRACE("Resetting all memory pools");
/* 重置静态池 */
for (int i = 0; i < MM_POOL_CUSTOM; i++) {
if (g_mm.static_initialized[i] && g_mm.static_pools[i]) {
size_t slot_size = g_default_configs[i].slot_size;
size_t slot_count = g_default_configs[i].slot_count;
mm_block_t* prev = NULL;
uint8_t* base_ptr = g_mm.static_pools[i];
g_mm.static_used_count[i] = 0;
g_mm.static_free_count[i] = slot_count;
memset(g_mm.static_slot_used[i], 0, sizeof(g_mm.static_slot_used[i]));
for (size_t j = 0; j < slot_count; j++) {
uint8_t* slot_start = base_ptr + j * slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
block->next = NULL;
block->ref_count = 0;
block->magic = MM_MAGIC;
block->size = slot_size - sizeof(mm_block_t);
block->pool_id = i;
block->slot_idx = j;
block->alloc_line = 0;
block->alloc_file = NULL;
block->user_ptr = (uint8_t*)slot_start + sizeof(mm_block_t);
if (prev) {
prev->next = block;
} else {
g_mm.static_free_lists[i] = block;
}
prev = block;
}
MM_TRACE("Reset static pool %d (%s)", i, g_default_configs[i].name);
}
}
/* 重置动态池 */
for (int i = 0; i < g_mm.dynamic_pool_count; i++) {
dynamic_pool_t* pool = &g_mm.dynamic_pools[i];
if (pool->initialized && pool->base_ptr) {
size_t slot_size = pool->config.slot_size;
size_t slot_count = pool->config.slot_count;
mm_block_t* prev = NULL;
pool->used_count = 0;
pool->free_count = slot_count;
if (pool->slot_used) {
memset(pool->slot_used, 0, slot_count * sizeof(uint32_t));
}
pool->alloc_count = 0;
pool->free_count_total = 0;
pool->peak_alloc_bytes = 0;
for (size_t j = 0; j < slot_count; j++) {
uint8_t* slot_start = pool->base_ptr + j * slot_size;
mm_block_t* block = (mm_block_t*)slot_start;
block->next = NULL;
block->ref_count = 0;
block->magic = MM_MAGIC;
block->size = slot_size - sizeof(mm_block_t);
/* 关键修复:更新 pool_id 为当前索引 */
block->pool_id = MM_POOL_CUSTOM + i;
block->slot_idx = j;
block->alloc_line = 0;
block->alloc_file = NULL;
block->user_ptr = (uint8_t*)slot_start + sizeof(mm_block_t);
if (prev) {
prev->next = block;
} else {
pool->free_list = block;
}
prev = block;
}
MM_TRACE("Reset dynamic pool %d (%s)", i, pool->name);
}
}
/* 重置全局统计 */
g_mm.total_alloc_count = 0;
g_mm.total_free_count = 0;
g_mm.total_alloc_bytes = 0;
g_mm.total_leak_count = 0;
MM_TRACE("All pools reset complete");
}
3. Makefile
#============================================================================= # Makefile for Memory Manager Module #============================================================================= CC = gcc AR = ar CFLAGS = -Wall -Wextra -O2 -g -fPIC -DMM_DEBUG=1 INCLUDES = -I./include LDFLAGS = -lpthread # 目标文件 TARGET_LIB = libmm.a TARGET_SO = libmm.so TARGET_TEST = test_mm # 源文件 SRC_DIR = src OBJ_DIR = obj SRCS = $(wildcard $(SRC_DIR)/*.c) OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS)) # 测试源文件 TEST_SRC = tests/test_mm.c TEST_OBJ = $(OBJ_DIR)/test_mm.o #============================================================================= # 目标规则 #============================================================================= .PHONY: all clean test install all: $(TARGET_LIB) $(TARGET_SO) $(TARGET_TEST) $(OBJ_DIR): mkdir -p $(OBJ_DIR) $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ $(TARGET_LIB): $(OBJS) $(AR) rcs $@ $^ $(TARGET_SO): $(OBJS) $(CC) -shared -o $@ $^ $(LDFLAGS) $(TEST_OBJ): $(TEST_SRC) | $(OBJ_DIR) $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ $(TARGET_TEST): $(OBJS) $(TEST_OBJ) $(CC) -o $@ $^ $(LDFLAGS) test: $(TARGET_TEST) ./$(TARGET_TEST) clean: rm -rf $(OBJ_DIR) rm -f $(TARGET_LIB) $(TARGET_SO) $(TARGET_TEST) install: $(TARGET_LIB) $(TARGET_SO) mkdir -p $(DESTDIR)/usr/lib mkdir -p $(DESTDIR)/usr/include cp $(TARGET_LIB) $(DESTDIR)/usr/lib/ cp $(TARGET_SO) $(DESTDIR)/usr/lib/ cp include/mm.h $(DESTDIR)/usr/include/
4. 内存管理七字诀实现对照表
| 口诀 | 实现位置 | 说明 |
|---|---|---|
| ① 预分配,不动态 | g_tensor_pool[512*1024] |
编译时分配静态数组 |
| ② 定大小,不分片 | slot_size = 64KB |
每个槽位固定大小 |
| ③ 池化管理,零碎片 | free_list 链表 |
固定槽位复用 |
| ④ 谁申请,谁释放 | mm_alloc()/mm_free() |
配对使用 |
| ⑤ 引用计数,防泄漏 | ref_count 字段 |
自动管理,为0时释放 |
| ⑥ 对齐边界,性能优 | __attribute__((aligned(64))) |
64字节对齐 |
| ⑦ 栈优先,堆次之 | MM_STACK_ARRAY 宏 |
小数组用栈 |
| ⑧ 静态区,放常量 | g_default_configs[] |
配置常量放在只读段 |
5. 测试程序
/**
* @file test_mm.c
* @brief 内存管理模块单元测试 - 每个测试独立运行
*/
#include "mm.h"
#include <stdio.h>
#include <string.h>
/*============================================================================
* 测试辅助宏
*============================================================================*/
#define TEST_START(name) \
printf("\n╔════════════════════════════════════════════════════════════════╗\n"); \
printf("║ Test: %-50s ║\n", name); \
printf("╚════════════════════════════════════════════════════════════════╝\n")
#define TEST_ASSERT(cond, msg) \
do { \
if (!(cond)) { \
printf(" ✗ FAILED: %s\n", msg); \
return -1; \
} \
} while (0)
#define TEST_PASS() \
printf(" ✓ PASSED\n"); \
return 0
/*============================================================================
* 测试前置/后置处理
*============================================================================*/
/**
* @brief 每个测试前的准备:重置所有池
*/
static void test_setup(void) {
/* 确保 mm_init 已调用 */
static int init_done = 0;
if (!init_done) {
mm_init();
init_done = 1;
}
/* 重置所有池,清空状态 */
mm_reset_all();
}
/**
* @brief 每个测试后的清理:重置所有池
*/
static void test_teardown(void) {
mm_reset_all();
}
/*============================================================================
* 测试用例1: 初始化和反初始化
*============================================================================*/
static int test_init_deinit(void) {
mm_error_t err;
TEST_START("Initialization and Deinitialization");
test_setup();
err = mm_init();
TEST_ASSERT(err == MM_OK, "mm_init failed");
TEST_ASSERT(mm_is_initialized() == 1, "mm_is_initialized should return true");
err = mm_init();
TEST_ASSERT(err == MM_OK, "Double init should succeed");
mm_deinit();
TEST_ASSERT(mm_is_initialized() == 0, "mm_is_initialized should return false");
mm_deinit();
TEST_ASSERT(mm_is_initialized() == 0, "Double deinit should be safe");
mm_init();
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例2: 预定义池分配和释放
*============================================================================*/
static int test_predefined_pool_alloc_free(void) {
void* p1, *p2, *p3;
mm_error_t err;
mm_stats_t stats;
TEST_START("Predefined Pool - Alloc/Free");
test_setup();
mm_init();
p1 = mm_alloc(MM_POOL_TENSOR, 1024);
TEST_ASSERT(p1 != NULL, "Alloc 1KB from TensorPool failed");
p2 = mm_alloc(MM_POOL_TENSOR, 2048);
TEST_ASSERT(p2 != NULL, "Alloc 2KB from TensorPool failed");
p3 = mm_alloc(MM_POOL_TENSOR, 4096);
TEST_ASSERT(p3 != NULL, "Alloc 4KB from TensorPool failed");
TEST_ASSERT(mm_get_size(p1) == 1024, "Wrong size for p1");
TEST_ASSERT(mm_get_size(p2) == 2048, "Wrong size for p2");
TEST_ASSERT(mm_get_size(p3) == 4096, "Wrong size for p3");
TEST_ASSERT(mm_get_pool_id(p1) == MM_POOL_TENSOR, "Wrong pool ID for p1");
err = mm_stats(MM_POOL_TENSOR, &stats);
TEST_ASSERT(err == MM_OK, "mm_stats failed");
TEST_ASSERT(stats.used_slots == 3, "Used slots should be 3");
mm_free(MM_POOL_TENSOR, p3);
mm_free(MM_POOL_TENSOR, p2);
mm_free(MM_POOL_TENSOR, p1);
err = mm_stats(MM_POOL_TENSOR, &stats);
TEST_ASSERT(err == MM_OK, "mm_stats failed");
TEST_ASSERT(stats.used_slots == 0, "Used slots should be 0 after free");
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例3: 池满处理
*============================================================================*/
static int test_pool_full(void) {
void* slots[10];
int i;
mm_stats_t stats;
TEST_START("Pool Full Handling");
test_setup();
mm_init();
for (i = 0; i < 8; i++) {
slots[i] = mm_alloc(MM_POOL_TENSOR, 1024);
if (slots[i] == NULL) {
printf(" ✗ FAILED: Alloc %d failed\n", i);
test_teardown();
return -1;
}
}
void* p = mm_alloc(MM_POOL_TENSOR, 1024);
TEST_ASSERT(p == NULL, "Alloc should fail when pool is full");
mm_free(MM_POOL_TENSOR, slots[0]);
p = mm_alloc(MM_POOL_TENSOR, 1024);
TEST_ASSERT(p != NULL, "Alloc after free should succeed");
mm_free(MM_POOL_TENSOR, p);
for (i = 1; i < 8; i++) {
mm_free(MM_POOL_TENSOR, slots[i]);
}
mm_stats(MM_POOL_TENSOR, &stats);
TEST_ASSERT(stats.used_slots == 0, "All slots should be free");
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例4: 引用计数
*============================================================================*/
static int test_refcount(void) {
void* p;
int ref;
mm_stats_t stats;
TEST_START("Reference Count");
test_setup();
mm_init();
p = mm_alloc(MM_POOL_TENSOR, 1024);
TEST_ASSERT(p != NULL, "Alloc failed");
ref = mm_refcount(p);
TEST_ASSERT(ref == 1, "Initial refcount should be 1");
ref = mm_ref(p);
TEST_ASSERT(ref == 2, "After ref, refcount should be 2");
ref = mm_ref(p);
TEST_ASSERT(ref == 3, "After second ref, refcount should be 3");
ref = mm_unref(MM_POOL_TENSOR, p);
TEST_ASSERT(ref == 2, "After unref, refcount should be 2");
ref = mm_unref(MM_POOL_TENSOR, p);
TEST_ASSERT(ref == 1, "After second unref, refcount should be 1");
ref = mm_unref(MM_POOL_TENSOR, p);
TEST_ASSERT(ref == 0, "After third unref, refcount should be 0");
p = NULL;
mm_stats(MM_POOL_TENSOR, &stats);
TEST_ASSERT(stats.used_slots == 0, "Memory should be freed");
test_teardown();
TEST_PASS();
}
/**
* @brief 测试动态池创建和使用
*/
static int test_dynamic_pool(void) {
int pool_id;
mm_error_t err;
void* p1, *p2;
mm_stats_t stats;
TEST_START("Dynamic Pool Creation");
test_setup();
mm_init();
err = mm_pool_create("TestPool", 16384, 4, &pool_id);
TEST_ASSERT(err == MM_OK, "mm_pool_create failed");
TEST_ASSERT(pool_id >= MM_POOL_CUSTOM, "Invalid pool ID");
p1 = mm_alloc(pool_id, 8192);
TEST_ASSERT(p1 != NULL, "Alloc from dynamic pool failed");
p2 = mm_alloc(pool_id, 4096);
TEST_ASSERT(p2 != NULL, "Second alloc from dynamic pool failed");
err = mm_stats(pool_id, &stats);
TEST_ASSERT(err == MM_OK, "mm_stats failed");
TEST_ASSERT(stats.used_slots == 2, "Used slots should be 2");
TEST_ASSERT(stats.total_slots == 4, "Total slots should be 4");
/* 释放分配的内存 */
mm_free(pool_id, p1);
mm_free(pool_id, p2);
/* 验证池已空 */
err = mm_stats(pool_id, &stats);
TEST_ASSERT(err == MM_OK, "mm_stats failed");
TEST_ASSERT(stats.used_slots == 0, "Pool should be empty before destroy");
/* 销毁池 */
err = mm_pool_destroy(pool_id);
TEST_ASSERT(err == MM_OK, "mm_pool_destroy failed");
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例6: 内存对齐检查
*============================================================================*/
static int test_alignment(void) {
void* p;
int i;
TEST_START("Memory Alignment");
test_setup();
mm_init();
size_t sizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096};
int num_sizes = sizeof(sizes) / sizeof(sizes[0]);
for (i = 0; i < num_sizes; i++) {
p = mm_alloc(MM_POOL_TENSOR, sizes[i]);
if (p == NULL) {
printf(" ✗ FAILED: Alloc %zu failed\n", sizes[i]);
test_teardown();
return -1;
}
uintptr_t addr = (uintptr_t)p;
if ((addr & (MM_ALIGNMENT - 1)) != 0) {
printf(" ✗ FAILED: Memory not aligned: %p (size=%zu)\n", p, sizes[i]);
mm_free(MM_POOL_TENSOR, p);
test_teardown();
return -1;
}
mm_free(MM_POOL_TENSOR, p);
}
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例7: 内存统计和转储
*============================================================================*/
static int test_stats_dump(void) {
void* p1, *p2;
mm_stats_t stats;
TEST_START("Statistics and Dump");
test_setup();
mm_init();
p1 = mm_alloc(MM_POOL_TENSOR, 1024);
TEST_ASSERT(p1 != NULL, "Alloc from TensorPool failed");
p2 = mm_alloc(MM_POOL_MODEL, 65536);
TEST_ASSERT(p2 != NULL, "Alloc from ModelPool failed");
mm_stats(MM_POOL_TENSOR, &stats);
TEST_ASSERT(stats.used_slots == 1, "TensorPool used slots should be 1");
TEST_ASSERT(stats.free_slots == 7, "TensorPool free slots should be 7");
mm_stats(MM_POOL_MODEL, &stats);
TEST_ASSERT(stats.used_slots == 1, "ModelPool used slots should be 1");
TEST_ASSERT(stats.free_slots == 3, "ModelPool free slots should be 3");
mm_dump(stdout);
mm_free(MM_POOL_TENSOR, p1);
mm_free(MM_POOL_MODEL, p2);
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例8: 内存泄漏检测
*============================================================================*/
static int test_leak_detection(void) {
void* p;
int leaks;
TEST_START("Leak Detection");
test_setup();
mm_init();
/* 分配内存但不释放 */
p = mm_alloc(MM_POOL_TENSOR, 1024);
TEST_ASSERT(p != NULL, "Alloc failed");
/* 检测泄漏 - 应该只检测到1个 */
leaks = mm_check_leaks(stdout);
if (leaks != 1) {
printf(" ✗ FAILED: Should detect 1 leak, got %d\n", leaks);
mm_free(MM_POOL_TENSOR, p);
test_teardown();
return -1;
}
/* 释放内存 */
mm_free(MM_POOL_TENSOR, p);
/* 再次检测 - 应该没有泄漏 */
leaks = mm_check_leaks(stdout);
if (leaks != 0) {
printf(" ✗ FAILED: Should detect 0 leaks, got %d\n", leaks);
test_teardown();
return -1;
}
test_teardown();
TEST_PASS();
}
/**
* @brief 测试错误处理
*/
static int test_error_handling(void) {
void* p;
size_t sz;
TEST_START("Error Handling");
test_setup();
mm_init();
/* 测试无效池ID - 应该返回NULL */
p = mm_alloc(999, 1024);
TEST_ASSERT(p == NULL, "Alloc with invalid pool should return NULL");
/* 测试释放无效指针 - 应该安全,不崩溃 */
mm_free(MM_POOL_TENSOR, (void*)0x1234);
mm_free(MM_POOL_TENSOR, NULL);
/* 测试分配超过槽位大小 - 应该返回NULL,不是段错误 */
p = mm_alloc(MM_POOL_TENSOR, 128 * 1024);
TEST_ASSERT(p == NULL, "Alloc larger than slot size should return NULL");
/* 测试获取无效指针的大小 */
sz = mm_get_size((void*)0x1234);
TEST_ASSERT(sz == 0, "mm_get_size on invalid pointer should return 0");
/* 测试获取无效指针的池ID */
int pid = mm_get_pool_id((void*)0x1234);
TEST_ASSERT(pid == -1, "mm_get_pool_id on invalid pointer should return -1");
test_teardown();
TEST_PASS();
}
/*============================================================================
* 测试用例10: 栈内存宏
*============================================================================*/
static int test_stack_macros(void) {
int i;
TEST_START("Stack Memory Macros");
test_setup();
MM_STACK_ARRAY(int, arr, 256);
for (i = 0; i < 256; i++) {
arr[i] = i;
}
if (arr[0] != 0 || arr[255] != 255) {
printf(" ✗ FAILED: Stack array initialization failed\n");
test_teardown();
return -1;
}
MM_STACK_INIT(float, farr, 128, 3.14f);
for (i = 0; i < 128; i++) {
if (farr[i] != 3.14f) {
printf(" ✗ FAILED: Stack init failed at index %d\n", i);
test_teardown();
return -1;
}
}
test_teardown();
TEST_PASS();
}
/*============================================================================
* 主函数
*============================================================================*/
int main(void) {
int passed = 0;
int failed = 0;
printf("\n");
printf("╔════════════════════════════════════════════════════════════════╗\n");
printf("║ Memory Manager Module Unit Tests ║\n");
printf("║ Memory Management Seven-Character Rhyme ║\n");
printf("╚════════════════════════════════════════════════════════════════╝\n");
/* 初始化一次 */
mm_init();
#define RUN_TEST(test) \
do { \
int ret = test(); \
if (ret == 0) passed++; \
else failed++; \
} while (0)
RUN_TEST(test_init_deinit);
RUN_TEST(test_predefined_pool_alloc_free);
RUN_TEST(test_pool_full);
RUN_TEST(test_refcount);
RUN_TEST(test_dynamic_pool);
RUN_TEST(test_alignment);
RUN_TEST(test_stats_dump);
RUN_TEST(test_leak_detection);
RUN_TEST(test_error_handling);
RUN_TEST(test_stack_macros);
printf("\n");
printf("╔════════════════════════════════════════════════════════════════╗\n");
printf("║ Test Summary ║\n");
printf("╠════════════════════════════════════════════════════════════════╣\n");
printf("║ Passed: %2d ║\n", passed);
printf("║ Failed: %2d ║\n", failed);
printf("╚════════════════════════════════════════════════════════════════╝\n");
mm_deinit();
return failed > 0 ? 1 : 0;
}
运行结果:

第二部分 内存管理模块设计与实现分析
一、项目背景与设计动机
项目场景:为什么要在项目中自己实现一个内存管理模块,而不是直接使用 malloc/free?
思路:
嵌入式 Linux 项目中,需要运行 AI 推理任务。这个场景有几个特殊要求:
-
实时性要求:AI 推理需要稳定的延迟,通常在 30-50ms 内完成。
malloc/free的不确定性(可能触发系统调用、内存碎片导致分配变慢)会破坏实时性。 -
内存受限:嵌入式设备内存有限(通常 1-2GB),频繁的
malloc/free会产生内存碎片,长时间运行后可能出现"有足够总内存但无法分配连续大块"的情况。 -
确定性分配:AI 推理中张量大小是固定的(如 224x224x3 的输入),因此希望分配时间恒定,不随运行时间变化。
-
共享内存需求:多个模块可能需要共享同一块张量数据(如预处理模块和推理模块),需要引用计数来管理生命周期。
基于这些考虑,需要设计了这个预分配、固定槽位、引用计数的内存管理模块。
二、内存管理七字诀的由来
项目场景:"内存管理七字诀"是从哪里来?
思路:
这七字诀是在嵌入式开发中总结的经验,每一条对应一个具体的实践原则:
╔═══════════════════════════════════════════════════════════════════════════════╗ ║ 内存管理七字诀 ║ ╠═══════════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ ① 预分配,不动态 ② 定大小,不分片 ③ 池化管理,零碎片 ║ ║ ④ 谁申请,谁释放 ⑤ 引用计数,防泄漏 ⑥ 对齐边界,性能优 ║ ║ ⑦ 栈优先,堆次之 ⑧ 静态区,放常量 ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════════╝
逐条解释:
| 口诀 | 含义 | 实现方式 |
|---|---|---|
| ① 预分配,不动态 | 内存池在编译时或初始化时一次性分配,运行时不再申请新内存 | static uint8_t g_tensor_pool[512*1024] 静态数组 |
| ② 定大小,不分片 | 每个池的槽位大小固定,避免内存碎片 | slot_size = 64KB,每个槽位固定 |
| ③ 池化管理,零碎片 | 使用槽位池管理,分配释放都在池内进行,无外部碎片 | free_list 空闲链表管理固定槽位 |
| ④ 谁申请,谁释放 | 明确内存所有权,申请者负责释放 | mm_alloc() / mm_free() 配对使用 |
| ⑤ 引用计数,防泄漏 | 支持引用计数,多个使用者共享时自动管理 | ref_count 字段,为0时自动释放 |
| ⑥ 对齐边界,性能优 | 内存按64字节对齐,缓存友好,适合SIMD指令 | __attribute__((aligned(64))) |
| ⑦ 栈优先,堆次之 | 小对象用栈,大对象用池 | MM_STACK_ARRAY 宏 |
| ⑧ 静态区,放常量 | 配置信息放在只读数据段 | g_default_configs[] 静态常量 |
三、核心数据结构与流程图
项目场景:内存管理的核心数据结构和分配释放流程图
思路:
3.1 核心数据结构图
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 全局内存池布局 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ g_tensor_pool[512KB] g_model_pool[1MB] g_frame_pool[256KB] │ │ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ Slot 0 (64KB) │ │ Slot 0 (256KB) │ │ Slot 0 (64KB) │ │ │ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │ │ │ │ mm_block_t │ │ │ │ mm_block_t │ │ │ │ mm_block_t │ │ │ │ │ │ (64 bytes) │ │ │ │ (64 bytes) │ │ │ │ (64 bytes) │ │ │ │ │ ├─────────────────┤ │ │ ├─────────────────┤ │ │ ├─────────────────┤ │ │ │ │ │ user_data │ │ │ │ user_data │ │ │ │ user_data │ │ │ │ │ │ (65472 bytes) │ │ │ │ (262080 bytes) │ │ │ │ (65472 bytes) │ │ │ │ │ └─────────────────┘ │ │ └─────────────────┘ │ │ └─────────────────┘ │ │ │ ├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤ │ │ │ Slot 1 (64KB) │ │ Slot 1 (256KB) │ │ Slot 1 (64KB) │ │ │ │ ... │ │ ... │ │ ... │ │ │ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ │ │ │ │ mm_block_t 结构 (64字节对齐): │ │ ┌────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┐ │ │ │ next │ref_count│ magic │ size │pool_id │slot_idx│alloc_line│user_ptr│ │ │ │ 8字节 │ 4字节 │ 4字节 │ 8字节 │ 4字节 │ 4字节 │ 4字节 │ 8字节 │ │ │ └────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┘ │ │ ↑ │ │ 64字节对齐,填充到64字节 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
3.2 分配流程图
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 内存分配流程 (mm_alloc) │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 开始: mm_alloc(pool_id, size) │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 1. 参数验证 │ │ │ │ - size == 0? → 返回 NULL │ │ │ │ - 模块未初始化? → 返回 NULL │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 2. 根据 pool_id 选择池 │ │ │ │ ├── pool_id < MM_POOL_CUSTOM → 静态池 │ │ │ │ └── pool_id >= MM_POOL_CUSTOM → 动态池 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 3. 从空闲链表取块 │ │ │ │ block = free_list │ │ │ │ if (block == NULL) → 返回 NULL (池满) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 4. 检查大小 │ │ │ │ if (size > block->size) → 返回 NULL (超过槽位容量) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 5. 更新池状态 │ │ │ │ - free_list = block->next │ │ │ │ - slot_used[slot_idx] = 1 │ │ │ │ - used_count++ │ │ │ │ - free_count-- │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 6. 初始化块头部 │ │ │ │ - block->ref_count = 1 │ │ │ │ - block->size = size (保存请求大小) │ │ │ │ - block->magic = MM_MAGIC │ │ │ │ - memset(user_ptr, 0, size) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 返回 user_ptr │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
3.3 释放流程图
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 内存释放流程 (mm_free) │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 开始: mm_free(pool_id, ptr) │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 1. 参数验证 │ │ │ │ - ptr == NULL? → 返回 │ │ │ │ - 模块未初始化? → 返回 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 2. 验证指针范围 │ │ │ │ - 检查 ptr 是否在池的地址范围内 │ │ │ │ - 不在范围内 → 返回 (安全) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 3. 获取块头部并验证 │ │ │ │ block = ptr - sizeof(mm_block_t) │ │ │ │ if (block->magic != MM_MAGIC) → 返回 (无效块) │ │ │ │ if (block->pool_id != pool_id) → 返回 (不属于此池) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 4. 检查双重释放 │ │ │ │ if (slot_used[slot_idx] == 0) → 返回 (已释放) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 5. 更新池状态 │ │ │ │ - slot_used[slot_idx] = 0 │ │ │ │ - used_count-- │ │ │ │ - free_count++ │ │ │ │ - block->size = 槽位容量 (恢复为完整容量) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 6. 加入空闲链表 │ │ │ │ block->next = free_list │ │ │ │ free_list = block │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 返回 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
四、适用场景分析
项目场景:这个内存管理模块适合哪些场景?不适合哪些场景?
思路:
4.1 适合的场景
| 场景 | 原因 | 示例 |
|---|---|---|
| 嵌入式AI推理 | 张量大小固定,预分配可避免碎片 | RK3576 NPU推理 |
| 实时系统 | 分配时间确定,无系统调用 | 工业控制、自动驾驶 |
| 多媒体处理 | 帧缓冲区大小固定 | 视频解码、图像处理 |
| 长时间运行服务 | 无内存碎片,可稳定运行数月 | 监控系统、服务器 |
| 多模块共享数据 | 引用计数自动管理生命周期 | 预处理→推理→后处理流水线 |
4.2 不适合的场景
| 场景 | 原因 | 替代方案 |
|---|---|---|
| 大小不固定的动态数据 | 槽位大小固定,无法分配大于槽位的数据 | 使用 malloc/free 或变长池 |
| 大量小对象分配 | 槽位较大,浪费内存 | 使用 slab 分配器 |
| 需要动态扩容 | 池大小在编译时确定 | 使用动态池 + 链表 |
| 跨进程共享内存 | 本模块设计为单进程内使用 | 使用共享内存 + 信号量 |
| 内存需求变化大的应用 | 预分配可能浪费或不足 | 使用堆分配器 |
五、可扩展性设计
项目场景:如果要扩展这个模块,支持更多功能,会怎么设计?
思路:
5.1 扩展点1:新增池类型
/* 在 mm.h 中添加新的池类型 */
typedef enum {
MM_POOL_TENSOR = 0,
MM_POOL_MODEL = 1,
MM_POOL_FRAME = 2,
MM_POOL_BUFFER = 3, /* 新增:通用缓冲区池 */
MM_POOL_CUSTOM = 4,
} mm_pool_type_t;
/* 在 mm.c 中添加配置 */
static const mm_pool_config_t g_default_configs[] = {
[MM_POOL_BUFFER] = {
.name = "BufferPool",
.total_size = 256 * 1024,
.slot_size = 32 * 1024,
.slot_count = 8,
.alignment = MM_ALIGNMENT,
.is_dynamic = 0,
},
/* ... */
};
5.2 扩展点2:支持变长槽位
/* 在 mm_pool_create 中增加标志位 */ mm_error_t mm_pool_create(const char* name, size_t slot_size, size_t slot_count, int flags, int* out_pool_id); /* flags 定义 */ #define MM_POOL_FIXED_SIZE 0x00 /* 固定大小槽位 */ #define MM_POOL_VARIABLE 0x01 /* 变长槽位 (使用最佳匹配算法) */ #define MM_POOL_GROWABLE 0x02 /* 可扩容池 */
5.3 扩展点3:支持内存统计导出
/* 导出 JSON 格式统计 */
char* mm_stats_export_json(void);
/* 示例输出 */
{
"pools": [
{
"name": "TensorPool",
"total_slots": 8,
"used_slots": 3,
"free_slots": 5,
"total_bytes": 524288,
"used_bytes": 196608
}
],
"global": {
"total_allocs": 1024,
"total_frees": 1000,
"current_leaks": 24
}
}
5.4 扩展点4:支持内存池快照和恢复
/* 保存当前状态 */ mm_error_t mm_snapshot(const char* path); /* 从快照恢复 (用于调试) */ mm_error_t mm_restore(const char* path);
5.5 扩展点5:支持线程局部池
/* 创建线程局部池 */ mm_error_t mm_tls_pool_create(const char* name, size_t slot_size, size_t slot_count, int* out_pool_id); /* 获取当前线程的池 */ mm_error_t mm_tls_pool_get(const char* name, int* out_pool_id);
六、设计权衡与取舍
项目场景:为什么选择这个方案而不是其他?
思路:
| 权衡点 | 选择 | 理由 |
|---|---|---|
| 固定槽位 vs 变长槽位 | 固定槽位 | 简单、无碎片、分配时间O(1);牺牲了灵活性 |
| 静态池 vs 动态池 | 两者都支持 | 核心数据用静态池,可选动态池扩展 |
| 引用计数 vs 手动释放 | 引用计数 | 避免共享数据的双重释放问题 |
| 64字节对齐 vs 默认对齐 | 64字节对齐 | 适合NEON/SIMD指令,缓存友好 |
| 全局锁 vs 无锁 | 池级锁 | 简化实现,嵌入式单线程场景够用 |
| 内存池大小 | 编译时固定 | 嵌入式系统内存确定,避免运行时决策 |
七、测试策略
项目场景:怎么测试这个模块的?测试用例覆盖了哪些场景?
思路实践:
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 测试用例覆盖图 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. test_init_deinit → 初始化和反初始化 │ │ 2. test_predefined_pool → 预定义池分配/释放 │ │ 3. test_pool_full → 池满处理 │ │ 4. test_refcount → 引用计数 │ │ 5. test_dynamic_pool → 动态池创建/销毁 │ │ 6. test_alignment → 64字节对齐验证 │ │ 7. test_stats_dump → 统计信息准确性 │ │ 8. test_leak_detection → 内存泄漏检测 │ │ 9. test_error_handling → 错误处理(无效指针等) │ │ 10. test_stack_macros → 栈内存宏 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
每个测试都包含:
-
正常路径:分配、使用、释放
-
边界条件:池满、空指针、无效参数
-
异常处理:双重释放、越界访问
八、总结
-
简化设计:如果项目不需要共享内存,可以去掉引用计数,简化一半代码
-
使用位图管理槽位:替代
slot_used数组,减少内存占用 -
增加池级别统计:方便排查内存问题
-
支持编译时配置:通过宏定义选择功能,减少二进制大小
-
增加单元测试覆盖率:达到95%以上
九、项目中的经验教训
-
内存管理没有银弹:不同的场景需要不同的策略,七字诀是在实践中总结的经验,但不是放之四海而皆准的真理。
-
测试驱动设计:复杂的内存管理必须有完善的单元测试,否则改一个问题可能引入三个新问题。
-
串行与并发的差异:锁机制是为了未来可能的并行扩展,这是提前设计。
-
简单就是美:最终能工作的版本,比一个完美的设计更有价值。不要过度工程化。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)