C - 内存缓存池2

核心知识点:内存管理、对齐、碎片整理、多线程安全。 升级方向: 支持不同大小的内存块(分级内存池),减少碎片; 实现内存对齐(如 8/16 字节对齐); 加锁保证多线程安全; 增加内存使用统计、泄漏检测等。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdint.h>
#include <time.h>

// ===================== 配置常量 =====================
#define ALIGNMENT 16                  // 内存对齐大小(16字节)
#define POOL_CLASSES 6                // 分级内存池的类别数
#define MAX_LEAK_CHECK_ITEMS 1024     // 泄漏检测最大记录数

// 分级内存池的块大小配置(按2的幂次递增,减少碎片)
static const size_t block_sizes[POOL_CLASSES] = {16, 32, 64, 128, 256, 512};

// ===================== 数据结构定义 =====================
/**
 * 单个分级内存池结构(对应一种块大小)
 */
typedef struct SubPool {
    size_t block_size;                // 该子池的块大小(已对齐)
    size_t block_count;               // 总块数
    size_t used_count;                // 已使用块数
    void* pool_start;                 // 内存池起始地址
    void* free_list;                  // 空闲块链表头
    pthread_mutex_t mutex;            // 子池互斥锁(线程安全)
} SubPool;

/**
 * 全局内存池管理器(整合所有分级子池)
 */
typedef struct MemPoolManager {
    SubPool sub_pools[POOL_CLASSES];  // 分级子池数组
    // 泄漏检测:记录分配的块(地址+分配时间+线程ID)
    struct {
        void* addr;
        pthread_t tid;
        time_t alloc_time;
    } alloc_records[MAX_LEAK_CHECK_ITEMS];
    int record_count;                 // 已记录的分配项数
    pthread_mutex_t record_mutex;     // 记录锁(保护alloc_records)
} MemPoolManager;

// ===================== 工具函数 =====================
/**
 * 内存对齐计算(向上对齐到ALIGNMENT的整数倍)
 */
static inline size_t align_size(size_t size) {
    return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
}

/**
 * 查找匹配的子池(根据申请大小找最合适的分级子池)
 */
static int find_sub_pool(size_t size) {
    for (int i = 0; i < POOL_CLASSES; i++) {
        if (block_sizes[i] >= size) {
            return i;
        }
    }
    return -1;  // 超过最大块大小,不支持
}

// ===================== 核心接口 =====================
/**
 * 初始化内存池管理器
 * @param manager 内存池管理器指针
 * @param block_counts 每个分级子池的块数(数组长度=POOL_CLASSES)
 * @return 0成功,-1失败
 */
int mem_pool_manager_init(MemPoolManager* manager, const size_t block_counts[POOL_CLASSES]) {
    if (manager == NULL || block_counts == NULL) {
        fprintf(stderr, "参数错误:空指针\n");
        return -1;
    }

    // 初始化全局记录锁
    if (pthread_mutex_init(&manager->record_mutex, NULL) != 0) {
        fprintf(stderr, "记录锁初始化失败\n");
        return -1;
    }
    manager->record_count = 0;
    memset(manager->alloc_records, 0, sizeof(manager->alloc_records));

    // 初始化每个分级子池
    for (int i = 0; i < POOL_CLASSES; i++) {
        SubPool* sub = &manager->sub_pools[i];
        size_t block_size = align_size(block_sizes[i]);  // 确保块大小对齐
        size_t block_count = block_counts[i];

        if (block_count == 0) {
            fprintf(stderr, "子池%d块数不能为0\n", i);
            return -1;
        }

        // 分配子池内存(总大小=块大小*块数)
        size_t total_size = block_size * block_count;
        sub->pool_start = malloc(total_size);
        if (sub->pool_start == NULL) {
            fprintf(stderr, "子池%d内存分配失败(需%zu字节)\n", i, total_size);
            // 回滚已分配的子池
            for (int j = 0; j < i; j++) {
                free(manager->sub_pools[j].pool_start);
                pthread_mutex_destroy(&manager->sub_pools[j].mutex);
            }
            return -1;
        }

        // 初始化子池属性
        sub->block_size = block_size;
        sub->block_count = block_count;
        sub->used_count = 0;
        sub->free_list = sub->pool_start;

        // 初始化子池互斥锁
        if (pthread_mutex_init(&sub->mutex, NULL) != 0) {
            fprintf(stderr, "子池%d锁初始化失败\n", i);
            free(sub->pool_start);
            for (int j = 0; j < i; j++) {
                free(manager->sub_pools[j].pool_start);
                pthread_mutex_destroy(&manager->sub_pools[j].mutex);
            }
            return -1;
        }

        // 构建空闲链表
        char* current = (char*)sub->pool_start;
        for (size_t j = 0; j < block_count - 1; j++) {
            void** next_ptr = (void**)current;
            *next_ptr = current + block_size;
            current += block_size;
        }
        *(void**)current = NULL;  // 最后一块指向NULL

        printf("子池%d初始化完成:块大小%zu字节,总块数%zu,总内存%zu字节\n",
               i, block_size, block_count, total_size);
    }

    printf("内存池管理器初始化成功(共%d个分级子池)\n", POOL_CLASSES);
    return 0;
}

/**
 * 分配内存块
 * @param manager 内存池管理器指针
 * @param size 申请的内存大小
 * @return 分配的内存地址(NULL表示失败)
 */
void* mem_pool_alloc(MemPoolManager* manager, size_t size) {
    if (manager == NULL || size == 0) {
        fprintf(stderr, "分配失败:参数错误\n");
        return NULL;
    }

    // 找到匹配的子池
    int pool_idx = find_sub_pool(size);
    if (pool_idx == -1) {
        fprintf(stderr, "分配失败:申请大小%zu超过最大块大小%zu\n", size, block_sizes[POOL_CLASSES-1]);
        return NULL;
    }

    SubPool* sub = &manager->sub_pools[pool_idx];
    void* alloc_block = NULL;

    // 加锁保证线程安全
    pthread_mutex_lock(&sub->mutex);

    // 检查空闲链表是否有可用块
    if (sub->free_list != NULL) {
        // 取出空闲链表头
        alloc_block = sub->free_list;
        void* next_block = *(void**)sub->free_list;
        sub->free_list = next_block;
        sub->used_count++;

        // 记录分配信息(用于泄漏检测)
        pthread_mutex_lock(&manager->record_mutex);
        if (manager->record_count < MAX_LEAK_CHECK_ITEMS) {
            manager->alloc_records[manager->record_count].addr = alloc_block;
            manager->alloc_records[manager->record_count].tid = pthread_self();
            manager->alloc_records[manager->record_count].alloc_time = time(NULL);
            manager->record_count++;
        } else {
            fprintf(stderr, "分配记录已满,无法记录新块%p\n", alloc_block);
        }
        pthread_mutex_unlock(&manager->record_mutex);
    }

    pthread_mutex_unlock(&sub->mutex);

    if (alloc_block == NULL) {
        fprintf(stderr, "子池%d无空闲块(申请大小%zu)\n", pool_idx, size);
    } else {
        printf("分配块:地址%p,子池%d,块大小%zu(申请大小%zu)\n",
               alloc_block, pool_idx, sub->block_size, size);
    }

    return alloc_block;
}

/**
 * 释放内存块
 * @param manager 内存池管理器指针
 * @param block 要释放的块地址
 * @return 0成功,-1失败
 */
int mem_pool_free(MemPoolManager* manager, void* block) {
    if (manager == NULL || block == NULL) {
        fprintf(stderr, "释放失败:空指针\n");
        return -1;
    }

    // 找到块所属的子池
    int pool_idx = -1;
    SubPool* sub = NULL;
    for (int i = 0; i < POOL_CLASSES; i++) {
        sub = &manager->sub_pools[i];
        char* start = (char*)sub->pool_start;
        char* end = start + sub->block_size * sub->block_count;
        if ((char*)block >= start && (char*)block < end) {
            pool_idx = i;
            break;
        }
    }

    if (pool_idx == -1) {
        fprintf(stderr, "释放失败:地址%p不在内存池范围内\n", block);
        return -1;
    }

    // 检查是否是合法的块起始地址
    size_t offset = (char*)block - (char*)sub->pool_start;
    if (offset % sub->block_size != 0) {
        fprintf(stderr, "释放失败:地址%p不是合法块起始地址\n", block);
        return -1;
    }

    // 加锁释放
    pthread_mutex_lock(&sub->mutex);

    // 将块插回空闲链表头
    *(void**)block = sub->free_list;
    sub->free_list = block;
    sub->used_count--;

    // 移除分配记录
    pthread_mutex_lock(&manager->record_mutex);
    for (int i = 0; i < manager->record_count; i++) {
        if (manager->alloc_records[i].addr == block) {
            // 覆盖当前记录(简单实现,有序场景可优化)
            memmove(&manager->alloc_records[i], &manager->alloc_records[i+1],
                    (manager->record_count - i - 1) * sizeof(manager->alloc_records[0]));
            manager->record_count--;
            break;
        }
    }
    pthread_mutex_unlock(&manager->record_mutex);

    pthread_mutex_unlock(&sub->mutex);

    printf("释放块:地址%p,子池%d,块大小%zu\n", block, pool_idx, sub->block_size);
    return 0;
}

/**
 * 检测内存泄漏
 * @param manager 内存池管理器指针
 */
void mem_pool_check_leak(const MemPoolManager* manager) {
    if (manager == NULL) {
        fprintf(stderr, "泄漏检测失败:内存池未初始化\n");
        return;
    }

    pthread_mutex_lock((pthread_mutex_t*)&manager->record_mutex);  // 去掉const(仅检测)
    printf("\n===== 内存泄漏检测报告 =====\n");
    if (manager->record_count == 0) {
        printf("未检测到内存泄漏\n");
    } else {
        printf("检测到%d个未释放的内存块:\n", manager->record_count);
        for (int i = 0; i < manager->record_count; i++) {
            struct tm* tm = localtime(&manager->alloc_records[i].alloc_time);
            char time_str[64];
            strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm);
            printf("  块地址:%p,分配线程ID:%lu,分配时间:%s\n",
                   manager->alloc_records[i].addr,
                   (unsigned long)manager->alloc_records[i].tid,
                   time_str);
        }
    }
    printf("=============================\n");
    pthread_mutex_unlock((pthread_mutex_t*)&manager->record_mutex);
}

/**
 * 打印内存池状态(使用统计)
 * @param manager 内存池管理器指针
 */
void mem_pool_print_status(const MemPoolManager* manager) {
    if (manager == NULL) {
        printf("内存池未初始化\n");
        return;
    }

    printf("\n===== 内存池整体状态 =====\n");
    size_t total_blocks = 0, total_used = 0, total_free = 0;
    size_t total_memory = 0, used_memory = 0, free_memory = 0;

    for (int i = 0; i < POOL_CLASSES; i++) {
        const SubPool* sub = &manager->sub_pools[i];
        size_t free_blocks = sub->block_count - sub->used_count;

        total_blocks += sub->block_count;
        total_used += sub->used_count;
        total_free += free_blocks;

        total_memory += sub->block_size * sub->block_count;
        used_memory += sub->block_size * sub->used_count;
        free_memory += sub->block_size * free_blocks;

        printf("子池%d:块大小%zu字节,总块数%zu,已使用%zu,空闲%zu\n",
               i, sub->block_size, sub->block_count, sub->used_count, free_blocks);
    }

    printf("=============================\n");
    printf("总块数:%zu,已使用:%zu,空闲:%zu\n", total_blocks, total_used, total_free);
    printf("总内存:%zu字节,已使用:%zu字节,空闲:%zu字节\n",
           total_memory, used_memory, free_memory);
    printf("=============================\n\n");
}

/**
 * 销毁内存池管理器
 * @param manager 内存池管理器指针
 */
void mem_pool_manager_destroy(MemPoolManager* manager) {
    if (manager == NULL) {
        return;
    }

    // 销毁每个子池
    for (int i = 0; i < POOL_CLASSES; i++) {
        SubPool* sub = &manager->sub_pools[i];
        if (sub->pool_start != NULL) {
            free(sub->pool_start);
            sub->pool_start = NULL;
        }
        pthread_mutex_destroy(&sub->mutex);
        memset(sub, 0, sizeof(SubPool));
    }

    // 销毁记录锁
    pthread_mutex_destroy(&manager->record_mutex);
    memset(manager, 0, sizeof(MemPoolManager));

    printf("内存池管理器已销毁\n");
}

// ===================== 测试代码 =====================
// 线程函数:模拟多线程分配/释放
void* thread_func(void* arg) {
    MemPoolManager* manager = (MemPoolManager*)arg;
    void* blocks[5];

    // 分配5个块(随机大小)
    for (int i = 0; i < 5; i++) {
        size_t size = rand() % 500 + 1;  // 1~500字节
        blocks[i] = mem_pool_alloc(manager, size);
        if (blocks[i] == NULL) {
            fprintf(stderr, "线程%lu分配块%d失败\n", (unsigned long)pthread_self(), i);
        }
    }

    // 释放前3个块
    for (int i = 0; i < 3; i++) {
        if (blocks[i] != NULL) {
            mem_pool_free(manager, blocks[i]);
        }
    }

    // 故意保留2个块不释放(模拟泄漏)
    return NULL;
}

int main(void) {
    // 1. 初始化内存池(每个子池的块数配置)
    size_t block_counts[POOL_CLASSES] = {10, 10, 10, 10, 10, 10};  // 每个子池10个块
    MemPoolManager manager;
    if (mem_pool_manager_init(&manager, block_counts) != 0) {
        fprintf(stderr, "内存池初始化失败\n");
        return -1;
    }

    // 2. 打印初始状态
    mem_pool_print_status(&manager);

    // 3. 多线程测试(创建3个线程)
    pthread_t t1, t2, t3;
    pthread_create(&t1, NULL, thread_func, &manager);
    pthread_create(&t2, NULL, thread_func, &manager);
    pthread_create(&t3, NULL, thread_func, &manager);

    // 等待线程结束
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);

    // 4. 打印线程执行后的状态
    mem_pool_print_status(&manager);

    // 5. 检测内存泄漏(预期6个未释放块)
    mem_pool_check_leak(&manager);

    // 6. 销毁内存池
    mem_pool_manager_destroy(&manager);

    return 0;
}
相关推荐
cchjyq1 天前
嵌入式按键调参:简洁接口轻松调参(ADC FLASH 按键 屏幕参数显示)
c语言·c++·单片机·mcu·开源·开源软件
无限进步_1 天前
【C语言】堆(Heap)的数据结构与实现:从构建到应用
c语言·数据结构·c++·后端·其他·算法·visual studio
xyd陈宇阳1 天前
C 语言宏定义(#define)语法与用法大全
c语言·嵌入式硬件
黎雁·泠崖1 天前
【线性表系列入门篇】从顺序表到链表:解锁数据结构的进化密码
c语言·数据结构·链表
JeffDingAI1 天前
【CANN训练营】在CANN8.5上体验Hello World开启Ascend C学习
c语言·开发语言·人工智能·学习
松涛和鸣1 天前
45、无依赖信息查询系统(C语言+SQLite3+HTML)
c语言·开发语言·数据库·单片机·sqlite·html
苦藤新鸡1 天前
2.字母异位词分组
c语言·c++·力扣·哈希算法
CryptoRzz1 天前
印度交易所 BSE 与 NSE 实时数据 API 接入指南
java·c语言·python·区块链·php·maven·symfony
枫叶丹41 天前
【Qt开发】Qt系统(三)->事件过滤器
java·c语言·开发语言·数据库·c++·qt