v17.04 鸿蒙内核源码分析(物理内存) | 怎么管理物理内存 原创
子曰:“志于道,据于德,依于仁,游于艺。” 《论语》:述而篇
百篇博客系列篇.本篇为:
v17.xx 鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存
内存管理相关篇为:
- v11.03 鸿蒙内核源码分析(内存分配) | 内存有哪些分配方式
- v12.04 鸿蒙内核源码分析(内存管理) | 虚拟内存全景图是怎样的
- v14.02 鸿蒙内核源码分析(内存汇编) | 谁是虚拟内存实现的基础
- v15.03 鸿蒙内核源码分析(内存映射) | 虚拟内存虚在哪里
- v16.02 鸿蒙内核源码分析(内存规则) | 内存管理到底在管什么
- v17.04 鸿蒙内核源码分析(物理内存) | 怎么管理物理内存
如何初始化物理内存?
鸿蒙内核物理内存采用了段页式管理,先看两个主要结构体.结构体的每个成员变量的含义都已经注解出来,请结合源码理解.
理解它们是理解物理内存管理的关键,尤其是 **LosVmPage ,**鸿蒙内存模块代码通篇都能看到它的影子.内核默认最大允许管理32个段.
段页式管理简单说就是先将物理内存切成一段段,每段再切成单位为 4K 的物理页框, 页是在内核层的操作单元, 物理内存的分配,置换,缺页,内存共享,文件高速缓存的读写,都是以页为单位的,所以LosVmPage 很重要,很重要!
结构体的每个变量代表了一个个的功能点, 结构体中频繁了出现LOS_DL_LIST的身影,双向链表是鸿蒙内核最重要的结构体,在系列篇开篇就专门讲过它的重要性.
再比如 LosVmPage.refCounts 页被引用的次数,可理解被进程拥有的次数,当refCounts大于1时,被多个进程所拥有,说明这页就是共享页.当等于0时,说明没有进程在使用了,这时就可以被释放了.
看到这里熟悉JAVA的同学是不是似曾相识,这像是Java的内存回收机制.在内核层面,引用的概念不仅仅适用于内存模块,也适用于其他模块,比如文件/设备模块,同样都存在共享的场景.这些模块不在这里展开说,后续有专门的章节细讲.
段一开始是怎么划分的 ? 需要方案提供商手动配置,存在静态的全局变量中,鸿蒙默认只配置了一段.
有了段和这些全局变量,就可以对内存初始化了. OsVmPageStartup 是对物理内存的初始化, 它被整个系统内存初始化 OsSysMemInit所调用. 直接上代码.
结合中文注释,代码很好理解, 此番操作之后全局变量里的值就都各就各位了,可以开始工作了.
如何分配/回收物理内存? 答案是伙伴算法
伙伴算法系列篇中有说过好几篇,这里再看图理解下什么伙伴算法,伙伴算法注重物理内存的连续性,注意是连续性!
结合图比如,要分配4(2^2)页(16k)的内存空间,算法会先从free_area2中查看free链表是否为空,如果有空闲块,则从中分配,如果没有空闲块,就从它的上一级free_area3(每块32K)中分配出16K,并将多余的内存(16K)加入到free_area2中去。如果free_area3也没有空闲,则从更上一级申请空间,依次递推,直到free_area max_order,如果顶级都没有空间,那么就报告分配失败。
释放是申请的逆过程,当释放一个内存块时,先在其对于的free_area链表中查找是否有伙伴存在,如果没有伙伴块,直接将释放的块插入链表头。如果有或板块的存在,则将其从链表摘下,合并成一个大块,然后继续查找合并后的块在更大一级链表中是否有伙伴的存在,直至不能合并或者已经合并至最大块2^max_order为止。
看过系列篇文章的可能都发现了,笔者喜欢用讲故事和打比方来说明内核运作机制, 为了更好的理解,同样打个比方, 笔者认为伙伴算法很像是卖标准猪肉块的算法.
物理内存是一整头猪,已经切成了1斤1斤的了,但是还都连在一起,每一斤上都贴了个标号, 而且老板只按 1斤(2^0), 2斤(2^1), 4斤(22),…256斤(28)的方式来卖.售货柜上分成了9组
张三来了要7斤猪肉,怎么办? **给8斤,注意是给8斤啊 ,因为它要严格按它的标准来卖.**张三如果归还了,查看现有8斤组里有没有序号能连在一块的,有的话2个8斤合成16斤,放到16斤组里去. 如果没有这8斤猪肉将挂到上图中第2组(2^3)再卖.
大家脑海中有画面了吗? 那么问题来了,它为什么要这么卖猪肉,好处是什么? 简单啊:至少两个好处:
第一:卖肉速度快,效率高,标准化的东西最好卖了.
第二:可防止碎肉太多,后面的人想买大块的猪肉买不到了. 请仔细想想是不是这样的?如果每次客户来了要多少就割多少出去,运行一段时候后你还能买到10斤连在一块的猪肉吗? 很可能给是一包碎肉,里面甚至还有一两一两的边角肉,碎肉的结果必然是管理麻烦,效率低啊.如果按伙伴算法的结果是运行一段时候后,图中0,1,2各组中都有可卖的猪肉啊,张三哥归还了那8斤(其实他指向要7斤)猪肉,王五兄弟来了要6斤,直接把张三哥归还的给王五就行了.效率极高.
那么问题又来了,凡事总有两面性,它的坏处是什么? 也简单啊 :至少两个坏处:
第一:浪费了!,白给的三斤对王五没用啊,浪费的问题有其他办法解决,但不是在这个层面去解决,而是由 slab分配器解决,这里不重点说后续会专门讲slab分配器是如何解决这个问题的.
第二:合并要求太严格了,一定得是伙伴(连续)才能合并成更大的块.这样也会导致时间久了很难有大块的连续性的猪肉块.
比方打完了,鸿蒙内核是如何实现卖肉算法的呢? 请看代码
为了方便理解代码细节, 这里说一种情况: 比如三哥要买3斤的,发现4斤,8斤的都没有了,只有16斤的怎么办? 注意不会给16斤,只会给4斤.这时需要把肉劈开,劈成 8,4,4,其中4斤给张三哥,将剩下的8斤,4斤挂到对应链表上. OsVmPhysPagesSpiltUnsafe 干的就是劈猪肉的活.
伙伴算法的链表是怎么初始化的,再看段代码
鸿蒙是面向未来设计的系统,高瞻远瞩,格局远大,设计精良, 海量知识点, 对内核源码加上中文注解已有三个多月,越深入精读内核源码,越能感受到设计者的精巧用心,创新突破, 向开发者致敬. 可以毫不夸张的说鸿蒙内核源码可作为大学C语言,数据结构,操作系统,汇编语言 四门课程的教学项目.如此宝库,不深入研究实在是暴殄天物,于心不忍.
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
辛苦了,给你的奖励
[图片已删除](请不要发布政治敏感类图片,谢谢)