洞察探索open banking如何通过小程序容器技术助力金融企业实现数据安全和数字化转型
895
2022-10-11
v86.01 鸿蒙内核源码分析(静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码
本篇关键词:池头、池体、节头、节块
静态分配
相比动态分配,静态内存池的分配就是个小弟弟,非常的简单,两个结构体 + 一张图 就能说明白。
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;
关于软定时器可以查看系列相关篇,请想想为何软件定时器会使用静态内存。
百文说内核 | 抓住主脉络
按功能模块:
基础知识 | 进程管理 | 任务管理 | 内存管理 |
双向链表 内核概念 源码结构 地址空间 计时单位 优雅的宏 钩子框架 位图管理 POSIX main函数 | 调度故事 进程控制块 进程空间 线性区 红黑树 进程管理 Fork进程 进程回收 Shell编辑 Shell解析 | 任务控制块 并发并行 就绪队列 调度机制 任务管理 用栈方式 软件定时器 控制台 远程登录 协议栈 | 内存规则 物理内存 内存概念 虚实映射 页表管理 静态分配 TLFS算法 内存池管理 原子操作 圆整对齐 |
通讯机制 | 文件系统 | 硬件架构 | 内核汇编 |
通讯总览 自旋锁 互斥锁 快锁使用 快锁实现 读写锁 信号量 事件机制 信号生产 信号消费 消息队列 消息封装 消息映射 共享内存 | 文件概念 文件故事 索引节点 VFS 文件句柄 根文件系统 挂载机制 管道文件 文件映射 写时拷贝 | 芯片模式 ARM架构 指令集 协处理器 工作模式 寄存器 多核管理 中断概念 中断管理 | 编码方式 汇编基础 汇编传参 链接脚本 开机启动 进程切换 任务切换 中断切换 异常接管 缺页中断 |
编译运行 | 调测工具 | ||
编译过程 编译构建 GN语法 忍者无敌 ELF格式 ELF解析 静态链接 重定位 动态链接 进程映像 应用启动 系统调用 VDSO |
据说喜欢点赞分享的,后来都成了大神。:)
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~