os_mem_pool.c
2026/7/20大约 5 分钟附录源码附录
os_mem_pool.c
路径: kernel\source\os_mem_pool.c
功能: 内存池(Memory Pool)实现。内存池是一种固定大小块的内存分配器,预先将一大块内存划分为固定大小的块,通过单向链表管理空闲块。分配和释放都是 O(1) 操作,非常适合频繁分配/释放小内存块的场景,如网络数据包缓冲区、USB 传输缓冲区等。
核心数据结构:
os_mempool_t: 内存池控制块,包含start_addr(池起始地址)、blk_size(块大小)、blk_total_num(总块数)、blk_free_num(空闲块数)、free_list(空闲块链表头)blk_head: 每个空闲块的头部,仅包含一个next指针,指向下一个空闲块mempool_check_tag: 可选的内存块校验标签,包含魔数(MEMPOOL_MAGIC_NUMBER = 0x504F4F4C)和 used 标记
关键 API:
| 函数 | 说明 |
|---|---|
os_mempool_init | 静态初始化内存池(用户提供控制块和内存区域) |
os_mempool_deinit | 去初始化:释放所有阻塞任务,从资源链表移除 |
os_mempool_alloc | 分配一个内存块:从 free_list 取头节点,支持阻塞等待 |
os_mempool_free | 释放内存块:归还到 free_list 头部,唤醒阻塞任务 |
os_mempool_info | 获取内存池信息(块大小、总数、空闲数) |
关键设计点:
- O(1) 分配/释放: free_list 是单向链表,分配取头节点,释放插回头部,无需遍历
- 块大小对齐:
blk_size自动向上对齐到OS_ALIGN_SIZE,可选地加上MEMPOOL_TAG_SIZE校验标签 - 校验标签(可选): 启用
OS_USING_MEMPOOL_CHECK_TAG后在每个块头部添加魔数0x504F4F4C("POOL"),free 时校验防止非法释放 - 阻塞等待:
os_mempool_alloc支持 timeout 参数,无空闲块时任务可阻塞等待 - SMP 安全: 支持
os_spin_lock保护多核并发访问
Shell 命令: show_mempool — 查看所有内存池信息,包括总块数、空闲块数、块大小、阻塞任务列表
/**
***********************************************************************************************************************
* Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @file os_mem_pool.c
*
* @brief This file implements the memory pool functions.
*
* @revision
* Date Author Notes
* 2020-11-18 OneOS team First Version
***********************************************************************************************************************
*/
#include <string.h>
#include <os_memory.h>
#include <os_errno.h>
#include <os_clock.h>
#include <os_spinlock.h>
#include "os_kernel_internal.h"
#ifdef OS_USING_MEM_POOL
#define MEMPOOL_TAG "MEMPOOL"
#ifdef OS_USING_MEMPOOL_CHECK_TAG
struct mempool_check_tag
{
uint32_t magic_number : 31;
uint32_t used_flag : 1;
};
#define MEMPOOL_MAGIC_NUMBER 0x504F4F4C
#define MEMPOOL_TAG_SIZE sizeof(struct mempool_check_tag)
#define MEMPOOL_SIZE_WITH_TAG(size) ((size) + MEMPOOL_TAG_SIZE)
#define MEMPOOL_PTR_WITHOUT_TAG(mem) ((void *)((char *)(mem) + MEMPOOL_TAG_SIZE))
#define MEMPOOL_PTR_WITH_TAG(mem) ((void *)((char *)(mem) - MEMPOOL_TAG_SIZE))
#define MEMPOOL_SET_BLK_TAG(mem) \
do \
{ \
((struct mempool_check_tag *)(mem))->magic_number = MEMPOOL_MAGIC_NUMBER; \
((struct mempool_check_tag *)(mem))->used_flag = OS_TRUE; \
} while (0)
#define MEMPOOL_BLK_TAG_OK(mem) \
((((struct mempool_check_tag *)(mem))->magic_number == MEMPOOL_MAGIC_NUMBER) && \
(((struct mempool_check_tag *)(mem))->used_flag == OS_TRUE))
#define MEMPOOL_GET_ALIGN_BLK_SIZE(blk_size) OS_ALIGN_UP(MEMPOOL_SIZE_WITH_TAG(blk_size), OS_ALIGN_SIZE)
#else
#define MEMPOOL_TAG_SIZE 0UL
#define MEMPOOL_GET_ALIGN_BLK_SIZE(blk_size) OS_ALIGN_UP((blk_size), OS_ALIGN_SIZE)
#endif
struct blk_head
{
struct blk_head *next;
};
static os_list_node_t gs_os_mempool_resource_list_head = OS_LIST_INIT(gs_os_mempool_resource_list_head);
static OS_DEFINE_SPINLOCK(gs_os_mempool_resource_list_lock);
static os_err_t _k_mempool_add_resourcelist(os_mempool_t *mempool, uint8_t object_alloc_type)
{
os_list_node_t *pos;
os_mempool_t *item_mp;
os_err_t ret;
OS_UNREFERENCE(object_alloc_type);
ret = OS_SUCCESS;
os_spin_lock(&gs_os_mempool_resource_list_lock);
os_list_for_each(pos, &gs_os_mempool_resource_list_head)
{
item_mp = os_list_entry(pos, os_mempool_t, resource_node);
if (item_mp == mempool)
{
os_spin_unlock(&gs_os_mempool_resource_list_lock);
OS_KERN_LOG(KERN_ERROR, MEMPOOL_TAG, "The mp(addr: %p) already exist", item_mp);
ret = OS_INVAL;
break;
}
}
if (OS_SUCCESS == ret)
{
os_list_add_tail(&gs_os_mempool_resource_list_head, &mempool->resource_node);
os_spin_unlock(&gs_os_mempool_resource_list_lock);
}
return ret;
}
static void _k_mempool_init_free_list(os_mempool_t *mempool)
{
struct blk_head *blk;
uint32_t i;
mempool->free_list = mempool->start_addr;
blk = (struct blk_head *)mempool->start_addr;
for (i = 0; i < (mempool->blk_total_num - 1UL); i++)
{
blk->next = (struct blk_head *)((char *)blk + mempool->blk_size);
blk = blk->next;
}
blk->next = OS_NULL;
}
static void _k_mempool_init(os_mempool_t *mempool, const char *name, void *start, os_size_t size, os_size_t blk_size)
{
if (OS_NULL != name)
{
(void)strncpy(&mempool->name[0], name, OS_NAME_MAX);
mempool->name[OS_NAME_MAX] = '\0';
}
else
{
mempool->name[0] = '\0';
}
mempool->start_addr = start;
mempool->size = size;
mempool->blk_size = blk_size;
mempool->blk_total_num = (size / blk_size);
mempool->blk_free_num = mempool->blk_total_num;
_k_mempool_init_free_list(mempool);
os_list_init(&mempool->task_list_head);
#ifdef OS_USING_SMP
os_spin_lock_init(&mempool->lock);
#endif
mempool->object_inited = OS_KOBJ_INITED;
}
os_mempool_id
os_mempool_init(os_mempool_dummy_t *mempool_cb, const char *name, void *start, os_size_t size, os_size_t blk_size)
{
void *start_addr;
os_mempool_t *mempool;
os_err_t ret;
OS_ASSERT(OS_FALSE == os_is_irq_active());
OS_ASSERT(mempool_cb);
OS_ASSERT(start);
OS_ASSERT(size >= OS_ALIGN_SIZE);
OS_ASSERT(blk_size > 0);
mempool = OS_TYPE_CONVERT(os_mempool_t *, mempool_cb);
ret = OS_SUCCESS;
start_addr = (void *)OS_ALIGN_UP((os_ubase_t)start, OS_ALIGN_SIZE);
size = OS_ALIGN_DOWN((os_ubase_t)start + size - (os_ubase_t)start_addr, OS_ALIGN_SIZE);
blk_size = MEMPOOL_GET_ALIGN_BLK_SIZE(blk_size);
if (0 != (size / blk_size))
{
ret = _k_mempool_add_resourcelist(mempool, OS_ALLOC_TYPE_STATIC);
if (OS_SUCCESS == ret)
{
_k_mempool_init(mempool, name, start_addr, size, blk_size);
}
else
{
mempool = OS_NULL;
}
}
else
{
mempool = OS_NULL;
}
return OS_TYPE_CONVERT(os_mempool_id, mempool);
}
os_err_t os_mempool_deinit(os_mempool_id mempool_id)
{
os_bool_t need_sched;
os_mempool_t *mempool;
OS_KERNEL_INIT();
mempool = OS_TYPE_CONVERT(os_mempool_t *, mempool_id);
OS_ASSERT(mempool);
OS_ASSERT(OS_KOBJ_INITED == mempool->object_inited);
OS_ASSERT(OS_FALSE == os_is_irq_active());
os_spin_lock(&gs_os_mempool_resource_list_lock);
os_list_del(&mempool->resource_node);
os_spin_unlock(&gs_os_mempool_resource_list_lock);
#ifdef OS_USING_SMP
os_spin_lock_irq(&mempool->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
mempool->object_inited = OS_KOBJ_DEINITED;
need_sched = k_cancel_all_blocked_task(&mempool->task_list_head);
#ifdef OS_USING_SMP
os_spin_unlock_irq(&mempool->lock, irq_save);
if (OS_TRUE == need_sched)
{
OS_KERNEL_ENTER();
OS_KERNEL_EXIT_SCHED();
}
#else
if (OS_TRUE == need_sched)
{
OS_KERNEL_EXIT_SCHED();
}
else
{
OS_KERNEL_EXIT();
}
#endif
return OS_SUCCESS;
}
void *os_mempool_alloc(os_mempool_id mempool_id, os_tick_t timeout)
{
void *mem;
os_tick_t tick_before;
os_tick_t tick_elapse;
os_err_t ret;
os_mempool_t *mempool;
OS_KERNEL_INIT();
mempool = OS_TYPE_CONVERT(os_mempool_t *, mempool_id);
OS_ASSERT(mempool);
OS_ASSERT(OS_KOBJ_INITED == mempool->object_inited);
OS_ASSERT((OS_NO_WAIT == timeout) || (OS_NULL == k_task_self()) || (OS_FALSE == os_is_irq_active()));
OS_ASSERT((OS_NO_WAIT == timeout) || (OS_NULL == k_task_self()) || (OS_FALSE == os_is_irq_disabled()));
OS_ASSERT((OS_NO_WAIT == timeout) || (OS_NULL == k_task_self()) || (OS_FALSE == os_is_schedule_locked()));
OS_ASSERT((timeout < (OS_TICK_MAX / 2)) || (OS_WAIT_FOREVER == timeout));
mem = OS_NULL;
ret = OS_SUCCESS;
#ifdef OS_USING_SMP
os_spin_lock_irq(&mempool->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
while ((mempool->free_list) == OS_NULL)
{
if (OS_NO_WAIT == timeout)
{
ret = OS_NOMEM;
break;
}
OS_ASSERT((OS_NULL != _k_task_self()));
tick_before = os_tick_get_value();
ret = k_block_task(&mempool->lock, irq_save, &mempool->task_list_head, timeout, OS_TRUE);
if (OS_SUCCESS != ret)
{
ret = OS_FAILURE;
return mem;
}
#ifdef OS_USING_SMP
os_spin_lock_irq(&mempool->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
if (OS_WAIT_FOREVER != timeout)
{
tick_elapse = os_tick_get_value() - tick_before;
timeout = (tick_elapse >= timeout) ? OS_NO_WAIT : (timeout - tick_elapse);
}
}
if (OS_SUCCESS == ret)
{
mem = mempool->free_list;
mempool->free_list = ((struct blk_head *)mem)->next;
mempool->blk_free_num--;
#ifdef OS_USING_SMP
os_spin_unlock_irq(&mempool->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
#ifdef OS_USING_MEMPOOL_CHECK_TAG
MEMPOOL_SET_BLK_TAG(mem);
mem = MEMPOOL_PTR_WITHOUT_TAG(mem);
#endif
}
else
{
#ifdef OS_USING_SMP
os_spin_unlock_irq(&mempool->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
}
return mem;
}
void os_mempool_free(os_mempool_id mempool_id, void *mem)
{
os_task_t *task;
os_bool_t need_sched;
os_mempool_t *mempool;
OS_KERNEL_INIT();
mempool = OS_TYPE_CONVERT(os_mempool_t *, mempool_id);
OS_ASSERT(mempool);
OS_ASSERT(OS_KOBJ_INITED == mempool->object_inited);
OS_ASSERT(mem);
OS_ASSERT((mem >= mempool->start_addr) && ((char *)mem < ((char *)mempool->start_addr + mempool->size)));
#ifdef OS_USING_MEMPOOL_CHECK_TAG
mem = MEMPOOL_PTR_WITH_TAG(mem);
OS_ASSERT(MEMPOOL_BLK_TAG_OK(mem));
#endif
OS_ASSERT((((os_size_t)((char *)mem - (char *)mempool->start_addr) % mempool->blk_size) == 0));
#ifdef OS_USING_SMP
os_spin_lock_irq(&mempool->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
((struct blk_head *)mem)->next = mempool->free_list;
mempool->free_list = mem;
mempool->blk_free_num++;
#ifdef OS_USING_SMP
k_kernel_spin_lock();
#endif
if (!os_list_empty(&mempool->task_list_head))
{
task = os_list_first_entry(&mempool->task_list_head, os_task_t, task_node);
need_sched = k_unblock_task(task);
}
else
{
need_sched = OS_FALSE;
}
#ifdef OS_USING_SMP
k_kernel_spin_unlock();
os_spin_unlock_irq(&mempool->lock, irq_save);
if (OS_TRUE == need_sched)
{
OS_KERNEL_ENTER();
OS_KERNEL_EXIT_SCHED();
}
#else
if (OS_TRUE == need_sched)
{
OS_KERNEL_EXIT_SCHED();
}
else
{
OS_KERNEL_EXIT();
}
#endif
}
void os_mempool_info(os_mempool_id mempool_id, os_mpinfo_t *info)
{
os_mempool_t *mempool;
OS_KERNEL_INIT();
mempool = OS_TYPE_CONVERT(os_mempool_t *, mempool_id);
OS_ASSERT(mempool);
OS_ASSERT(OS_KOBJ_INITED == mempool->object_inited);
OS_ASSERT(info);
#ifdef OS_USING_SMP
os_spin_lock_irq(&mempool->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
info->blk_size = mempool->blk_size - MEMPOOL_TAG_SIZE;
info->blk_total_num = mempool->blk_total_num;
info->blk_free_num = mempool->blk_free_num;
#ifdef OS_USING_SMP
os_spin_unlock_irq(&mempool->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
}
#if defined(OS_USING_SHELL)
#include <shell.h>
#define SH_SHOW_TASK_CNT_MAX 10
typedef struct
{
os_mempool_t *mempool;
os_size_t blk_total_num;
os_size_t blk_free_num;
os_size_t blk_size;
uint16_t block_task_count;
block_task_info_t block_info[SH_SHOW_TASK_CNT_MAX];
} sh_mempool_info_t;
static os_err_t os_mempool_show(os_mempool_t *mp)
{
sh_mempool_info_t mp_info;
os_task_t *iter_task;
uint16_t task_index;
OS_KERNEL_INIT();
if ((OS_NULL == mp) || (OS_KOBJ_INITED != mp->object_inited))
{
os_kprintf("The input parameter is an illegal mempool object.\r\n");
return OS_FAILURE;
}
#ifdef OS_USING_SMP
os_spin_lock_irq(&mp->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
mp_info.mempool = mp;
mp_info.blk_total_num = mp->blk_total_num;
mp_info.blk_free_num = mp->blk_free_num;
mp_info.blk_size = mp->blk_size;
mp_info.block_task_count = os_list_len(&mp->task_list_head);
task_index = 0;
os_list_for_each_entry(iter_task, &mp->task_list_head, os_task_t, task_node)
{
mp_info.block_info[task_index].current_priority = iter_task->current_priority;
mp_info.block_info[task_index].name = iter_task->name;
task_index++;
if (task_index >= SH_SHOW_TASK_CNT_MAX)
{
break;
}
}
#ifdef OS_USING_SMP
os_spin_unlock_irq(&mp->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
os_kprintf("%-*s %-10u %-10u %-10u ",
OS_NAME_MAX,
(mp->name[0] != '\0') ? mp->name : "-",
mp_info.blk_total_num,
mp_info.blk_free_num,
mp_info.blk_size - MEMPOOL_TAG_SIZE);
if (mp_info.block_task_count > 0)
{
os_kprintf("%-6u:", mp_info.block_task_count);
k_show_blocked_task(mp_info.block_info, task_index);
if (mp_info.block_task_count > task_index)
{
os_kprintf("/...\r\n");
}
else
{
os_kprintf("\r\n");
}
}
else
{
os_kprintf("%-6u\r\n", 0);
}
return OS_SUCCESS;
}
static os_err_t sh_show_mempool_info(int32_t argc, char *const *argv)
{
os_mempool_t *iter_mp;
os_mempool_t *mp_tmp;
uint16_t len;
OS_UNREFERENCE(argc);
OS_UNREFERENCE(argv);
os_kprintf("%-*s %-10s %-10s %-9s %-10s\r\n",
OS_NAME_MAX,
"MemPool",
"TotalCount",
"FreeCount",
"PerSize",
"Block Task");
len = OS_NAME_MAX;
while ((len--) != 0)
{
os_kprintf("-");
}
os_kprintf(" ---------- ---------- ---------- ----------\r\n");
if (argc >= 2)
{
if ((argv[1]) != OS_NULL)
{
mp_tmp = (os_mempool_t *)strtoul(argv[1], OS_NULL, 0);
os_spin_lock(&gs_os_mempool_resource_list_lock);
os_list_for_each_entry(iter_mp, &gs_os_mempool_resource_list_head, os_mempool_t, resource_node)
{
if (mp_tmp == iter_mp)
{
os_mempool_show(mp_tmp);
os_spin_unlock(&gs_os_mempool_resource_list_lock);
return OS_SUCCESS;
}
}
os_spin_unlock(&gs_os_mempool_resource_list_lock);
}
return OS_FAILURE;
}
os_spin_lock(&gs_os_mempool_resource_list_lock);
os_list_for_each_entry(iter_mp, &gs_os_mempool_resource_list_head, os_mempool_t, resource_node)
{
os_mempool_show(iter_mp);
}
os_spin_unlock(&gs_os_mempool_resource_list_lock);
return OS_SUCCESS;
}
SH_CMD_EXPORT(show_mempool, sh_show_mempool_info, "Show mempool information");
#endif
#endif