> 文档中心 > v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码

v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码


🚀 优质资源分享 🚀

学习路线指引(点击解锁) 知识定位 人群定位
🧡 Python实战微信订餐小程序 🧡 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

本篇关键词:池头池体节头节块

v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码

内存管理相关篇为:

  • v31.02 鸿蒙内核源码分析(内存规则) | 内存管理到底在管什么
  • v32.04 鸿蒙内核源码分析(物理内存) | 真实的可不一定精彩
  • v33.04 鸿蒙内核源码分析(内存概念) | RAM & ROM & Flash
  • v34.03 鸿蒙内核源码分析(虚实映射) | 映射是伟大的发明
  • v35.02 鸿蒙内核源码分析(页表管理) | 映射关系保存在哪
  • v36.03 鸿蒙内核源码分析(静态分配) | 很简单的一位小朋友
  • v37.01 鸿蒙内核源码分析(TLFS算法) | 图表解读TLFS原理
  • v38.01 鸿蒙内核源码分析(内存池管理) | 如何高效切割合并内存块
  • v39.04 鸿蒙内核源码分析(原子操作) | 谁在守护指令执行的完整性
  • v40.01 鸿蒙内核源码分析(圆整对齐) | 正在制作中 …

静态分配

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

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;

下图来源于官网
v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 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 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;}

申请

///从指定的静态内存池中申请一块静态内存块,整个内核源码只有 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);//返回可用的虚拟地址}

释放

/// 释放指定的一块静态内存块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;}

使用

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

///软时钟初始化 ,注意函数在多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 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 swtmrSortLink); LOS_ListInit(&srq->swtmrHandlerQueue); srq->swtmrTask = NULL;    }    SwtmrDebugDataInit();    return LOS_OK;}
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;

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

百文说内核 | 抓住主脉络

  • 百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切。
  • 与代码需不断debug一样,文章内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
  • 百文在 站点发布,鸿蒙研究站 | weharmonyos 中回复 百文 可方便阅读。
  • v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码

按功能模块:

基础知识 进程管理 任务管理 内存管理
双向链表内核概念源码结构地址空间计时单位优雅的宏钩子框架位图管理POSIXmain函数 调度故事进程控制块进程空间线性区红黑树进程管理Fork进程进程回收Shell编辑Shell解析 任务控制块并发并行就绪队列调度机制任务管理用栈方式软件定时器控制台远程登录协议栈 内存规则物理内存内存概念虚实映射页表管理静态分配TLFS算法内存池管理原子操作圆整对齐
通讯机制 文件系统 硬件架构 内核汇编
通讯总览自旋锁互斥锁快锁使用快锁实现读写锁信号量事件机制信号生产信号消费消息队列消息封装消息映射共享内存 文件概念文件故事索引节点VFS文件句柄根文件系统挂载机制管道文件文件映射写时拷贝 芯片模式ARM架构指令集协处理器工作模式寄存器多核管理中断概念中断管理 编码方式汇编基础汇编传参链接脚本开机启动进程切换任务切换中断切换异常接管缺页中断
编译运行 调测工具
编译过程编译构建GN语法忍者无敌ELF格式ELF解析静态链接重定位动态链接进程映像应用启动系统调用VDSO 模块监控日志跟踪系统安全测试用例

百万注源码 | 处处扣细节

  • 百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。
  • 四大码仓推送 | 同步官方源码,鸿蒙研究站 | weharmonyos 中回复 百万 可方便阅读。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KlkFIC0L-1653498222761)(https://gitee.com/weharmony/kernel_liteos_a_note/widgets/widget_card.svg?colors=393222,ebdfc1,fffae5,d8ca9f,393222,a28b40)]

据说喜欢点赞分享的,后来都成了大神。😃