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;
下图来源于官网

解读
- 静态内存池在概念上由 池头 和 池体 两部分组成,池体由众多节块组成,节块由 节头和 节体 两部分组成
- 在数据结构上表现为
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;
}
申请
///从指定的静态内存池中申请一块静态内存块,整个内核源码只有 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 < 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;
}
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代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。 - 百文在 < 鸿蒙研究站 | 开源中国 | 博客园 | 51cto | csdn | 知乎 | 掘金 > 站点发布,鸿蒙研究站 | weharmonyos 中回复 百文 可方便阅读。

按功能模块:
百万注源码 | 处处扣细节
百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。
< gitee | github | coding | gitcode > 四大码仓推送 | 同步官方源码,鸿蒙研究站 | weharmonyos 中回复 百万 可方便阅读。
据说喜欢点赞分享的,后来都成了大神。
v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码的更多相关文章
- 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析OpenHarmony源码 | v54.01
百篇博客系列篇.本篇为: v54.xx 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 51.c.h.o 下图是一个可执行文件编译,链接的过程. 本篇将通过一个完整的小工程来阐述E ...
- 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视GCC编译全过程 | 百篇博客分析OpenHarmony源码| v57.01
百篇博客系列篇.本篇为: v57.xx 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视编译全过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...
- 鸿蒙内核源码分析(进程镜像篇)|ELF是如何被加载运行的? | 百篇博客分析OpenHarmony源码 | v56.01
百篇博客系列篇.本篇为: v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...
- 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人 | 百篇博客分析OpenHarmony源码 | v55.01
百篇博客系列篇.本篇为: v55.xx 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程 ...
- v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口 | 百篇博客分析OpenHarmony源码
子曰:"苟正其身矣,于从政乎何有?不能正其身,如正人何?" <论语>:子路篇 百篇博客系列篇.本篇为: v72.xx 鸿蒙内核源码分析(Shell解析篇) | 应用窥视 ...
- v75.01 鸿蒙内核源码分析(远程登录篇) | 内核如何接待远方的客人 | 百篇博客分析OpenHarmony源码
子曰:"不学礼,无以立 ; 不学诗,无以言 " <论语>:季氏篇 百篇博客分析.本篇为: (远程登录篇) | 内核如何接待远方的客人 设备驱动相关篇为: v67.03 ...
- v76.01 鸿蒙内核源码分析(共享内存) | 进程间最快通讯方式 | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(共享内存篇) | 进程间最快通讯方式 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) | 同样 ...
- v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(用户态锁篇) | 如何使用快锁Futex(上) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...
- v77.01 鸿蒙内核源码分析(消息封装篇) | 剖析LiteIpc(上)进程通讯内容 | 新的一年祝大家生龙活虎 虎虎生威
百篇博客分析|本篇为:(消息封装篇) | 剖析LiteIpc进程通讯内容 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁 ...
随机推荐
- 《CSS 揭秘》作者Lea Verou:我喜欢分享开源的行业文化
本文仅用于学习和交流,不用于商业目的.非商业转载请注明作译者.出处,并保留本文的原始链接:http://www.ituring.com.cn/art... 访谈嘉宾: Lea VerouW3C CSS ...
- 【每日日报】第四十七天---<div>
1 学习HTML HTML <div> 元素是块级元素,它可用于组合其他 HTML 元素的容器. <div> 元素没有特定的含义.除此之外,由于它属于块级元素,浏览器会在其前后 ...
- Wireshark捕获网易云音乐音频文件地址
打开Wireshark,开始捕获. 打开网易云音乐,然后播放一首歌. Wireshark停时捕获,然后在不活的文件中搜索字符串"mp3".可以发现有如下信息: 将其中的内容:&qu ...
- 关键字static、extern、volatile、详解及举例
一.预备知识 1. 什么是局部变量?什么是全局变量? 所谓局部变量,就是指在函数内部定义的变量的,只在该函数范围内有效. 全局变量是指,在函数外部定义的变量为外部变量,即全局变量.它 ...
- IDEA小技巧:Markdown里的命令行可以直接运行了
作为一名开发者,相信大部分人都喜欢用Markdown来写文章和写文档. 如果你经常用开源项目或者自己维护开源项目,肯定对于项目下的README文件也相当熟悉了吧,通常我们会在这里介绍项目的功能.如何使 ...
- 新华三Gen10服务器ilo5中刷新bios固件
新华三Gen10服务器ilo5中刷新bios固件. 当前bios1.42 已经是最新了. 固件下载后解压缩. 选择刷新固件. 点击浏览.flash文件. 点击flash 点击ok确认 开始上传 刷新进 ...
- [AcWing 768] 忽略大小写比较字符串大小
点击查看代码 #include<iostream> using namespace std; string a, b; int main() { getline(cin, a); getl ...
- Git在项目中使用技巧
1.常用的命令 mkdir 文件夹名 创建文件夹 clear 清楚屏幕 ls或者ll 将当前目录下的子文件和子目录平铺在控制台 find 目录名 将对应目录下的子孙文件或子孙目录平铺在控制台 rm 文 ...
- 机器学习-学习笔记(一) --> (假设空间 & 版本空间)及 归纳偏好
机器学习 一.机器学习概念 啥是机器学习 机器学习:假设用P来评估计算机程序在某任务类T上的性能,若一个程序通过利用经验E在T中任务上获得了性能改善,则关于T和P,该程序对E进行了学习 通俗讲:通过计 ...
- jfinal极速开发
下载jfinal项目,上面都配置好了不用自己新建从头配置.https://jfinal.com/ idea打开项目 配置数据库 resources目录下demo-config-dev.txt # co ...