静态分配

相比动态分配,静态内存池的分配就是个小弟弟,非常的简单,两个结构体 + 一张图 就能说明白。

typedef struct {//静态内存池信息结构体
    UINT32 uwBlkSize;           /**< Block size | 块大小*/
    UINT32 uwBlkNum;            /**< Block number | 块数量*/
    UINT32 uwBlkCnt;            /**< The number of allocated blocks | 已经被分配的块数量*/
    LOS_MEMBOX_NODE stFreeList; /**< Free list | 空闲链表*/
} LOS_MEMBOX_INFO;

typedef struct tagMEMBOX_NODE { //内存池中空闲节点的结构,是个单向的链表
    struct tagMEMBOX_NODE *pstNext; /**< Free node's pointer to the next node in a memory pool | 指向内存池中下一个空闲节点的指针*/
} LOS_MEMBOX_NODE;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

下图来源于官网

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_openharmony

解读

  • 静态内存池在概念上由 池头 和 池体 两部分组成,池体由众多节块组成,节块由 节头和 节体 两部分组成
  • 在数据结构上表现为 LOS_MEMBOX_INFO(池头) + [LOS_MEMBOX_NODE(节头) + data(节体)] + … + [LOS_MEMBOX_NODE(节头) + data(节体)] ,在虚拟地址上它们是连在一起的。
  • 池头 记录总信息,包括 节块大小,总节块数量,已分配节块数量,空闲节块链表表头,stFreeList将所有空闲节块链接到一起,分配内存根本不需要遍历,stFreeList指向的下一个不为null代表还有空闲节块。
  • 节头只有一个指向下一个空闲链表的pstNext指针,简单但足以。
  • 静态分配的优缺点是很明显的,总结下:
  • 负责管理的结构体简单,会占用很少的空间,这点优于动态分配。
  • 分配速度最快,一步到位。
  • 缺点是浪费严重,僵硬不灵活,很计划经济,给每一个家庭每月口粮就这么多,高矮胖瘦都不会管。

因代码量不大,但很精彩,看这种代码是种享受,本篇详细列出静态内存代码层面的实现,关键处已添加注释。

初始化
///初始化一个静态内存池,根据入参设定其起始地址、总大小及每个内存块大小
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MemboxInit(VOID *pool, UINT32 poolSize, UINT32 blkSize)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;//在内存起始处放置控制头
    LOS_MEMBOX_NODE *node = NULL;
    //...
    UINT32 index;
    UINT32 intSave;
    MEMBOX_LOCK(intSave);
    boxInfo->uwBlkSize = LOS_MEMBOX_ALIGNED(blkSize + OS_MEMBOX_NODE_HEAD_SIZE); //节块总大小(节头+节体)
    boxInfo->uwBlkNum = (poolSize - sizeof(LOS_MEMBOX_INFO)) / boxInfo->uwBlkSize;//总节块数量
    boxInfo->uwBlkCnt = 0;	//已分配的数量
    if (boxInfo->uwBlkNum == 0) {//只有0块的情况
        MEMBOX_UNLOCK(intSave);
        return LOS_NOK;
    }
    node = (LOS_MEMBOX_NODE *)(boxInfo + 1);//去除池头,找到第一个节块位置
    boxInfo->stFreeList.pstNext = node;//池头空闲链表指向第一个节块
    for (index = 0; index < boxInfo->uwBlkNum - 1; ++index) {//切割节块,挂入空闲链表
        node->pstNext = OS_MEMBOX_NEXT(node, boxInfo->uwBlkSize);//按块大小切割好,统一由pstNext指向
        node = node->pstNext;//node存储了下一个节点的地址信息
    }
    node->pstNext = NULL;//最后一个为null
    MEMBOX_UNLOCK(intSave);
    return LOS_OK;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
申请
///从指定的静态内存池中申请一块静态内存块,整个内核源码只有 OsSwtmrScan中用到了静态内存.
LITE_OS_SEC_TEXT VOID *LOS_MemboxAlloc(VOID *pool)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
    LOS_MEMBOX_NODE *node = NULL;
    LOS_MEMBOX_NODE *nodeTmp = NULL;
    UINT32 intSave;
    if (pool == NULL) {
        return NULL;
    }
    MEMBOX_LOCK(intSave);
    node = &(boxInfo->stFreeList);//拿到空闲单链表
    if (node->pstNext != NULL) {//不需要遍历链表,因为这是空闲链表
        nodeTmp = node->pstNext;//先记录要使用的节点
        node->pstNext = nodeTmp->pstNext;//不再空闲了,把节点摘出去了.
        OS_MEMBOX_SET_MAGIC(nodeTmp);//为已使用的节块设置魔法数字
        boxInfo->uwBlkCnt++;//已使用块数增加
    }
    MEMBOX_UNLOCK(intSave);
    return (nodeTmp == NULL) ? NULL : OS_MEMBOX_USER_ADDR(nodeTmp);//返回可用的虚拟地址
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
释放
/// 释放指定的一块静态内存块
LITE_OS_SEC_TEXT UINT32 LOS_MemboxFree(VOID *pool, VOID *box)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
    UINT32 ret = LOS_NOK;
    UINT32 intSave;
    if ((pool == NULL) || (box == NULL)) {
        return LOS_NOK;
    }
    MEMBOX_LOCK(intSave);
    do {
        LOS_MEMBOX_NODE *node = OS_MEMBOX_NODE_ADDR(box);//通过节体获取节块首地址
        if (OsCheckBoxMem(boxInfo, node) != LOS_OK) {
            break;
        }
        node->pstNext = boxInfo->stFreeList.pstNext;//节块指向空闲链表表头
        boxInfo->stFreeList.pstNext = node;//空闲链表表头反指向它,意味节块排到第一,下次申请将首个分配它
        boxInfo->uwBlkCnt--;//已经使用的内存块减一
        ret = LOS_OK;
    } while (0);//将被编译时优化
    MEMBOX_UNLOCK(intSave);
    return ret;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
使用

鸿蒙内核目前只有软时钟处理使用了静态内存池,直接上代码

///软时钟初始化 ,注意函数在多CPU情况下会执行多次
STATIC UINT32 SwtmrBaseInit(VOID)
{
    UINT32 ret;
    UINT32 size = sizeof(SWTMR_CTRL_S) * LOSCFG_BASE_CORE_SWTMR_LIMIT;
    SWTMR_CTRL_S *swtmr = (SWTMR_CTRL_S *)LOS_MemAlloc(m_aucSysMem0, size); /* system resident resource */
    if (swtmr == NULL) {
        return LOS_ERRNO_SWTMR_NO_MEMORY;
    }
    (VOID)memset_s(swtmr, size, 0, size);//清0
    g_swtmrCBArray = swtmr;//软时钟
    LOS_ListInit(&g_swtmrFreeList);//初始化空闲链表
    for (UINT16 index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) {
            swtmr->usTimerID = index;//按顺序赋值
            LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->stSortList.sortLinkNode);//通过sortLinkNode将节点挂到空闲链表 
    }
	//想要用静态内存池管理,就必须要使用LOS_MEMBOX_SIZE来计算申请的内存大小,因为需要点前缀内存承载头部信息.
    size = LOS_MEMBOX_SIZE(sizeof(SwtmrHandlerItem), OS_SWTMR_HANDLE_QUEUE_SIZE);//规划一片内存区域作为软时钟处理函数的静态内存池。
    g_swtmrHandlerPool = (UINT8 *)LOS_MemAlloc(m_aucSysMem1, size); /* system resident resource */
    if (g_swtmrHandlerPool == NULL) {
        return LOS_ERRNO_SWTMR_NO_MEMORY;
    }
    ret = LOS_MemboxInit(g_swtmrHandlerPool, size, sizeof(SwtmrHandlerItem));
    if (ret != LOS_OK) {
        return LOS_ERRNO_SWTMR_HANDLER_POOL_NO_MEM;
    }
    for (UINT16 index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
        SwtmrRunQue *srq = &g_swtmrRunQue[index];
        /* The linked list of all cores must be initialized at core 0 startup for load balancing */
        OsSortLinkInit(&srq->swtmrSortLink);
        LOS_ListInit(&srq->swtmrHandlerQueue);
        srq->swtmrTask = NULL;
    }
    SwtmrDebugDataInit();
    return LOS_OK;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

typedef VOID (*SWTMR_PROC_FUNC)(UINTPTR arg);	//函数指针, 赋值给 SWTMR_CTRL_S->pfnHandler,回调处理
typedef struct {//处理软件定时器超时的回调函数的结构体
    SWTMR_PROC_FUNC handler;    /**< Callback function that handles software timer timeout  */	//处理软件定时器超时的回调函数
    UINTPTR arg;                /**< Parameter passed in when the callback function
                                     that handles software timer timeout is called */	//调用处理软件计时器超时的回调函数时传入的参数
    LOS_DL_LIST node;
#ifdef LOSCFG_SWTMR_DEBUG
    UINT32 swtmrID;
#endif
} SwtmrHandlerItem;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

关于软定时器可以查看系列相关篇,请想想为何软件定时器会使用静态内存。

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_嵌入式硬件_02

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_harmonyos_03

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_嵌入式硬件_04

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_嵌入式硬件_05

鸿蒙开发面试真题(含参考答案)

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_嵌入式硬件_06

OpenHarmony 开发环境搭建

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_鸿蒙内核_07

《OpenHarmony源码解析》
  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_openharmony_08

OpenHarmony 设备开发学习手册

鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友_嵌入式硬件_09

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。