UcOs-III 源码阅读: os_mem.c

lanlincmos / 2025-02-21 / 原文

//作用:固定大小内存管理器的代码,内存分区代码

/*
*********************************************************************************************************
*                                              uC/OS-III
*                                        The Real-Time Kernel
*
*                    Copyright 2009-2022 Silicon Laboratories Inc. www.silabs.com
*
*                                 SPDX-License-Identifier: APACHE-2.0
*
*               This software is subject to an open source license and is distributed by
*                Silicon Laboratories Inc. pursuant to the terms of the Apache License,
*                    Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                      内存分区管理
*
* 文件: os_mem.c
* 版本: V3.08.02
*********************************************************************************************************
*/

#define   MICRIUM_SOURCE
#include "os.h"

#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const  CPU_CHAR  *os_mem__c = "$Id: $";
#endif


#if (OS_CFG_MEM_EN > 0u)
/*
************************************************************************************************************************
*                                               创建内存分区
*
* 描述: 创建一个由 uC/OS-III 管理的固定大小的内存分区。
*
* 参数: p_mem    是指向用户内存空间中分配的内存分区控制块的指针。
*
*               p_name   是指向 ASCII 字符串的指针,用于为内存分区提供名称。
*
*               p_addr   是内存分区的起始地址。
*
*               n_blks   是从分区中创建的内存块数量。
*
*               blk_size 是内存分区中每个块的大小(以字节为单位)。
*
*               p_err    是指向包含错误消息的变量的指针,此函数会将其设置为以下之一:
*
*                            OS_ERR_NONE                    如果内存分区已正确创建
*                            OS_ERR_ILLEGAL_CREATE_RUN_TIME 如果在调用 OSSafetyCriticalStart() 之后尝试创建内存分区
*                            OS_ERR_MEM_CREATE_ISR          如果从 ISR 中调用此函数
*                            OS_ERR_MEM_INVALID_BLKS        用户指定的块数无效(必须 >= 2)
*                            OS_ERR_MEM_INVALID_P_ADDR      如果指定的内存分区存储地址无效,或者块未对齐到指针边界
*                            OS_ERR_MEM_INVALID_SIZE        用户指定的块大小无效
*                                                             - 必须大于指针的大小
*                                                             - 必须能够容纳整数个指针
*                            OS_ERR_OBJ_CREATED             如果内存分区已创建
* 返回: 无
*
* 注意: 无
************************************************************************************************************************
*/

void OSMemCreate(OS_MEM *p_mem, CPU_CHAR *p_name, void *p_addr, OS_MEM_QTY n_blks, OS_MEM_SIZE blk_size, OS_ERR *p_err)
{
#if (OS_CFG_ARG_CHK_EN > 0u)
    CPU_DATA align_msk;
#endif
    OS_MEM_QTY i;
    OS_MEM_QTY loops;
    CPU_INT08U *p_blk;
    void **p_link;
    CPU_SR_ALLOC();

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
        return;
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {  /* 不允许从 ISR 调用 */
        *p_err = OS_ERR_MEM_CREATE_ISR;
        return;
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_addr == (void *)0) {  /* 必须传递有效的内存分区地址 */
        *p_err = OS_ERR_MEM_INVALID_P_ADDR;
        return;
    }
    if (n_blks < 2u) {  /* 每个分区至少需要 2 个块 */
        *p_err = OS_ERR_MEM_INVALID_BLKS;
        return;
    }
    if (blk_size < sizeof(void *)) {  /* 必须包含至少一个指针的空间 */
        *p_err = OS_ERR_MEM_INVALID_SIZE;
        return;
    }
    align_msk = sizeof(void *) - 1u;
    if (align_msk > 0u) {
        if (((CPU_ADDR)p_addr & align_msk) != 0u) {  /* 必须对齐到指针大小 */
            *p_err = OS_ERR_MEM_INVALID_P_ADDR;
            return;
        }
        if ((blk_size & align_msk) != 0u) {  /* 块大小必须是地址大小的倍数 */
            *p_err = OS_ERR_MEM_INVALID_SIZE;
            return;
        }
    }
#endif

    p_link = (void **)p_addr;  /* 创建空闲内存块的链表 */
    p_blk = (CPU_INT08U *)p_addr;
    loops = n_blks - 1u;
    for (i = 0u; i < loops; i++) {
        p_blk += blk_size;
        *p_link = (void *)p_blk;  /* 在当前块中保存指向下一个块的指针 */
        p_link = (void **)(void *)p_blk;  /* 定位到下一个块 */
    }
    *p_link = (void *)0;  /* 最后一个内存块指向 NULL */

    CPU_CRITICAL_ENTER();
#if (OS_OBJ_TYPE_REQ > 0u)
#if (OS_CFG_OBJ_CREATED_CHK_EN > 0u)
    if (p_mem->Type == OS_OBJ_TYPE_MEM) {
        CPU_CRITICAL_EXIT();
        *p_err = OS_ERR_OBJ_CREATED;
        return;
    }
#endif
    p_mem->Type = OS_OBJ_TYPE_MEM;  /* 设置对象类型 */
#endif
#if (OS_CFG_DBG_EN > 0u)
    p_mem->NamePtr = p_name;  /* 保存内存分区的名称 */
#else
    (void)p_name;
#endif
    p_mem->AddrPtr = p_addr;  /* 存储内存分区的起始地址 */
    p_mem->FreeListPtr = p_addr;  /* 初始化指向空闲块池的指针 */
    p_mem->NbrFree = n_blks;  /* 存储 MCB 中的空闲块数量 */
    p_mem->NbrMax = n_blks;
    p_mem->BlkSize = blk_size;  /* 存储每个内存块的大小 */

#if (OS_CFG_DBG_EN > 0u)
    OS_MemDbgListAdd(p_mem);
    OSMemQty++;
#endif

    OS_TRACE_MEM_CREATE(p_mem, p_name);
    CPU_CRITICAL_EXIT();
    *p_err = OS_ERR_NONE;
}


/*
************************************************************************************************************************
*                                                  获取内存块
*
* 描述: 从内存分区中获取一个内存块。
*
* 参数: p_mem   是指向内存分区控制块的指针
*
*               p_err   是指向包含错误消息的变量的指针,此函数会将其设置为以下之一:
*
*                           OS_ERR_NONE               如果内存分区已正确创建
*                           OS_ERR_MEM_INVALID_P_MEM  如果传递了 NULL 指针作为 'p_mem'
*                           OS_ERR_MEM_NO_FREE_BLKS   如果没有更多的空闲内存块可以分配给调用者
*                           OS_ERR_OBJ_TYPE           如果 'p_mem' 指向的不是一个内存分区
*
* 返回: 如果没有检测到错误,返回指向内存块的指针
*              如果检测到错误,返回指向 NULL 的指针
*
* 注意: 无
************************************************************************************************************************
*/

void *OSMemGet(OS_MEM *p_mem, OS_ERR *p_err)
{
    void *p_blk;
    CPU_SR_ALLOC();

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((void *)0);
    }
#endif

    OS_TRACE_MEM_GET_ENTER(p_mem);

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_mem == (OS_MEM *)0) {  /* 必须指向一个有效的内存分区 */
        OS_TRACE_MEM_GET_FAILED(p_mem);
        OS_TRACE_MEM_GET_EXIT(OS_ERR_MEM_INVALID_P_MEM);
        *p_err = OS_ERR_MEM_INVALID_P_MEM;
        return ((void *)0);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_mem->Type != OS_OBJ_TYPE_MEM) {  /* 确保内存块已创建 */
        OS_TRACE_MEM_GET_EXIT(OS_ERR_OBJ_TYPE);
        *p_err = OS_ERR_OBJ_TYPE;
        return ((void *)0);
    }
#endif

    CPU_CRITICAL_ENTER();
    if (p_mem->NbrFree == 0u) {  /* 检查是否有空闲的内存块 */
        CPU_CRITICAL_EXIT();
        OS_TRACE_MEM_GET_FAILED(p_mem);
        OS_TRACE_MEM_GET_EXIT(OS_ERR_MEM_NO_FREE_BLKS);
        *p_err = OS_ERR_MEM_NO_FREE_BLKS;  /* 通知调用者内存分区为空 */
        return ((void *)0);  /* 返回 NULL 指针给调用者 */
    }
    p_blk = p_mem->FreeListPtr;  /* 指向下一个空闲的内存块 */
    p_mem->FreeListPtr = *(void **)p_blk;  /* 调整指针到新的空闲列表 */
    p_mem->NbrFree--;  /* 该分区中的内存块数量减少一个 */
    CPU_CRITICAL_EXIT();
    OS_TRACE_MEM_GET(p_mem);
    OS_TRACE_MEM_GET_EXIT(OS_ERR_NONE);
    *p_err = OS_ERR_NONE;  /* 没有错误 */
    return (p_blk);  /* 返回内存块给调用者 */
}


/*
************************************************************************************************************************
*                                                 释放内存块
*
* 描述: 将一个内存块返回到内存分区。
*
* 参数: p_mem    是指向内存分区控制块的指针
*
*               p_blk    是指向要释放的内存块的指针
*
*               p_err    是指向包含此函数返回的错误代码的变量的指针。
*
*                            OS_ERR_NONE               如果内存块已插入到分区中
*                            OS_ERR_MEM_FULL           如果将内存块返回到一个已经满的内存分区(释放的块比分配的多!)
*                            OS_ERR_MEM_INVALID_P_BLK  如果传递了 NULL 指针作为要释放的块
*                            OS_ERR_MEM_INVALID_P_MEM  如果传递了 NULL 指针作为 'p_mem'
*                            OS_ERR_OBJ_TYPE           如果 'p_mem' 指向的不是一个内存分区
*
* 返回: 无
*
* 注意: 无
************************************************************************************************************************
*/

void OSMemPut(OS_MEM *p_mem, void *p_blk, OS_ERR *p_err)
{
    CPU_SR_ALLOC();

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    OS_TRACE_MEM_PUT_ENTER(p_mem, p_blk);

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_mem == (OS_MEM *)0) {  /* 必须指向一个有效的内存分区 */
        OS_TRACE_MEM_PUT_FAILED(p_mem);
        OS_TRACE_MEM_PUT_EXIT(OS_ERR_MEM_INVALID_P_MEM);
        *p_err = OS_ERR_MEM_INVALID_P_MEM;
        return;
    }
    if (p_blk == (void *)0) {  /* 必须释放一个有效的块 */
        OS_TRACE_MEM_PUT_FAILED(p_mem);
        OS_TRACE_MEM_PUT_EXIT(OS_ERR_MEM_INVALID_P_BLK);
        *p_err = OS_ERR_MEM_INVALID_P_BLK;
        return;
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_mem->Type != OS_OBJ_TYPE_MEM) {  /* 确保内存块已创建 */
        OS_TRACE_MEM_PUT_EXIT(OS_ERR_OBJ_TYPE);
        *p_err = OS_ERR_OBJ_TYPE;
        return;
    }
#endif

    CPU_CRITICAL_ENTER();
    if (p_mem->NbrFree >= p_mem->NbrMax) {  /* 确保所有块未全部返回 */
        CPU_CRITICAL_EXIT();
        OS_TRACE_MEM_PUT_FAILED(p_mem);
        OS_TRACE_MEM_PUT_EXIT(OS_ERR_MEM_FULL);
        *p_err = OS_ERR_MEM_FULL;
        return;
    }
    *(void **)p_blk = p_mem->FreeListPtr;  /* 将释放的块插入到空闲块列表中 */
    p_mem->FreeListPtr = p_blk;
    p_mem->NbrFree++;  /* 该分区中的内存块数量增加一个 */
    CPU_CRITICAL_EXIT();
    OS_TRACE_MEM_PUT(p_mem);
    OS_TRACE_MEM_PUT_EXIT(OS_ERR_NONE);
    *p_err = OS_ERR_NONE;  /* 通知调用者内存块已释放 */
}


/*
************************************************************************************************************************
*                                           将内存分区添加到调试列表
*
* 描述: 此函数由 OSMemCreate() 调用,将内存分区添加到调试表中。
*
* 参数: p_mem    是指向内存分区的指针
*
* 返回: 无
*
* 注意: 此函数是 uC/OS-III 的内部函数,您的应用程序不应调用它。
************************************************************************************************************************
*/

#if (OS_CFG_DBG_EN > 0u)
void  OS_MemDbgListAdd (OS_MEM  *p_mem)
{
    p_mem->DbgPrevPtr               = (OS_MEM *)0;
    if (OSMemDbgListPtr == (OS_MEM *)0) {
        p_mem->DbgNextPtr           = (OS_MEM *)0;
    } else {
        p_mem->DbgNextPtr           =  OSMemDbgListPtr;
        OSMemDbgListPtr->DbgPrevPtr =  p_mem;
    }
    OSMemDbgListPtr                 =  p_mem;
}
#endif


/*
************************************************************************************************************************
*                                           初始化内存分区管理器
*
* 描述: 此函数由 uC/OS-III 调用,用于初始化内存分区管理器。您的应用程序必须不调用此函数。
*
* 参数: 无
*
* 返回: 无
*
* 注意: 此函数是 uC/OS-III 的内部函数,您的应用程序不应调用它。
************************************************************************************************************************
*/

void  OS_MemInit (OS_ERR  *p_err)
{
#if (OS_CFG_DBG_EN > 0u)
    OSMemDbgListPtr = (OS_MEM *)0;
    OSMemQty        = 0u;
#endif
   *p_err           = OS_ERR_NONE;
}
#endif