鸿蒙内核源码分析 (物理内存篇) | 怎么管理物理内存

如何初始化物理内存?

鸿蒙内核物理内存采用了段页式管理,先看两个主要结构体。结构体的每个成员变量的含义都已经注解出来,请结合源码理解.

#define VM_LIST_ORDER_MAX    9    //伙伴算法分组数量,从 2^0,2^1,...,2^8 (256*4K)=1M 
#define VM_PHYS_SEG_MAX    32    //最大支持32个段typedef struct VmPhysSeg {//物理段描述符PADDR_T start;            /* The start of physical memory area */ //物理内存段的开始地址size_t size;              /* The size of physical memory area */ //物理内存段的大小LosVmPage *pageBase;      /* The first page address of this area */ //本段首个物理页框地址SPIN_LOCK_S freeListLock; /* The buddy list spinlock */    //伙伴算法自旋锁,用于操作freeList上锁struct VmFreeList freeList[VM_LIST_ORDER_MAX];  /* The free pages in the buddy list */ //伙伴算法的分组,默认分成10组 2^0,2^1,...,2^VM_LIST_ORDER_MAXSPIN_LOCK_S lruLock;  //用于置换的自旋锁,用于操作lruListsize_t lruSize[VM_NR_LRU_LISTS];  //5个双循环链表大小,如此方便得到sizeLOS_DL_LIST lruList[VM_NR_LRU_LISTS]; //页面置换算法,5个双循环链表头,它们分别描述五中不同类型的链表
} LosVmPhysSeg;//注意: vmPage 中并没有虚拟地址,只有物理地址
typedef struct VmPage { //物理页框描述符LOS_DL_LIST         node;        /**< vm object dl list */ //虚拟内存节点,通过它挂/摘到全局g_vmPhysSeg[segID]->freeList[order]物理页框链表上UINT32              index;       /**< vm page index to vm object */ //索引位置PADDR_T             physAddr;    /**< vm page physical addr */  //物理页框起始物理地址,只能用于计算,不会用于操作(读/写数据==)Atomic              refCounts;   /**< vm page ref count */   //被引用次数,共享内存会被多次引用UINT32              flags;       /**< vm page flags */    //页标签,同时可以有多个标签(共享/引用/活动/被锁==)UINT8               order;       /**< vm page in which order list */ //被安置在伙伴算法的几号序列(              2^0,2^1,2^2,...,2^order)UINT8               segID;       /**< the segment id of vm page */ //所属段IDUINT16              nPages;      /**< the vm page is used for kernel heap */ //分配页数,标识从本页开始连续的几页将一块被分配
} LosVmPage;//注意:关于nPages和order的关系说明,当请求分配为5页时,order是等于3的,因为只有2^3才能满足5页的请求

理解它们是理解物理内存管理的关键,尤其是 LosVmPage , 鸿蒙内存模块代码通篇都能看到它的影子。内核默认最大允许管理 32 个段.

段页式管理简单说就是先将物理内存切成一段段,每段再切成单位为 4K 的物理页框, 页是在内核层的操作单元, 物理内存的分配,置换,缺页,内存共享,文件高速缓存的读写,都是以页为单位的,所以 LosVmPage 很重要,很重要!

结构体的每个变量代表了一个个的功能点, 结构体中频繁了出现 LOS_DL_LIST 的身影,双向链表是鸿蒙内核最重要的结构体,在系列篇开篇就专门讲过它的重要性.

再比如 LosVmPage.refCounts 页被引用的次数,可理解被进程拥有的次数,当 refCounts 大于 1 时,被多个进程所拥有,说明这页就是共享页。当等于 0 时,说明没有进程在使用了,这时就可以被释放了.

看到这里熟悉 JAVA 的同学是不是似曾相识,这像是 Java 的内存回收机制。在内核层面,引用的概念不仅仅适用于内存模块,也适用于其他模块,比如文件 / 设备模块,同样都存在共享的场景。这些模块不在这里展开说,后续有专门的章节细讲.

段一开始是怎么划分的?需要方案提供商手动配置,存在静态的全局变量中,鸿蒙默认只配置了一段.

struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX];//物理段数组,最大32段
INT32 g_vmPhysSegNum = 0; //总段数
LosVmPage *g_vmPageArray = NULL;//物理页框数组
size_t g_vmPageArraySize;//总物理页框数/* Physical memory area array */
STATIC struct VmPhysArea g_physArea[] = {//这里只有一个区域,即只生成一个段{.start = SYS_MEM_BASE, //整个物理内存基地址,#define SYS_MEM_BASE            DDR_MEM_ADDR ,  0x80000000.size = SYS_MEM_SIZE_DEFAULT,//整个物理内存总大小 0x07f00000},
};

有了段和这些全局变量,就可以对内存初始化了. OsVmPageStartup 是对物理内存的初始化, 它被整个系统内存初始化 OsSysMemInit 所调用.  直接上代码.

/******************************************************************************完成对物理内存整体初始化,本函数一定运行在实模式下1.申请大块内存g_vmPageArray存放LosVmPage,按4K一页划分物理内存存放在数组中.
******************************************************************************/
VOID OsVmPageStartup(VOID)
{struct VmPhysSeg *seg = NULL;LosVmPage *page = NULL;paddr_t pa;UINT32 nPage;INT32 segID;OsVmPhysAreaSizeAdjust(ROUNDUP((g_vmBootMemBase - KERNEL_ASPACE_BASE), PAGE_SIZE));//校正 g_physArea sizenPage = OsVmPhysPageNumGet();//得到 g_physArea 总页数g_vmPageArraySize = nPage * sizeof(LosVmPage);//页表总大小g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize);//实模式下申请内存,此时还没有初始化MMUOsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE));//OsVmPhysSegAdd();// 完成对段的初始化OsVmPhysInit();// 加入空闲链表和设置置换算法,LRU(最近最久未使用)算法for (segID = 0; segID < g_vmPhysSegNum; segID++) {//遍历物理段,将段切成一页一页seg = &g_vmPhysSeg[segID];nPage = seg->size >> PAGE_SHIFT;//本段总页数for (page = seg->pageBase, pa = seg->start; page <= seg->pageBase + nPage;//遍历,算出每个页框的物理地址page++, pa += PAGE_SIZE) {OsVmPageInit(page, pa, segID);//对物理页框进行初始化,注意每页的物理地址都不一样}OsVmPageOrderListInit(seg->pageBase, nPage);//伙伴算法初始化,将所有页加入空闲链表供分配}
}

结合中文注释,代码很好理解, 此番操作之后全局变量里的值就都各就各位了,可以开始工作了.

如何分配 / 回收物理内存? 答案是伙伴算法

伙伴算法系列篇中有说过好几篇,这里再看图理解下什么伙伴算法,伙伴算法注重物理内存的连续性,注意是连续性!

结合图比如,要分配 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 斤 (2^2),…256 斤 (2^8) 的方式来卖。售货柜上分成了 9 组

张三来了要 7 斤猪肉,怎么办? ** 给 8 斤,注意是给 8 斤啊 ,因为它要严格按它的标准来卖.** 张三如果归还了,查看现有 8 斤组里有没有序号能连在一块的,有的话 2 个 8 斤合成 16 斤,放到 16 斤组里去. 如果没有这 8 斤猪肉将挂到上图中第 2 组 (2^3) 再卖.

大家脑海中有画面了吗?那么问题来了,它为什么要这么卖猪肉,好处是什么?简单啊:至少两个好处:

第一:卖肉速度快,效率高,标准化的东西最好卖了.

第二:可防止碎肉太多,后面的人想买大块的猪肉买不到了. 请仔细想想是不是这样的?如果每次客户来了要多少就割多少出去,运行一段时候后你还能买到 10 斤连在一块的猪肉吗? 很可能给是一包碎肉,里面甚至还有一两一两的边角肉,碎肉的结果必然是管理麻烦,效率低啊。如果按伙伴算法的结果是运行一段时候后,图中 0,1,2 各组中都有可卖的猪肉啊,张三哥归还了那 8 斤 (其实他指向要 7 斤) 猪肉,王五兄弟来了要 6 斤,直接把张三哥归还的给王五就行了。效率极高.

那么问题又来了,凡事总有两面性,它的坏处是什么? 也简单啊:至少两个坏处:

第一:浪费了!,白给的三斤对王五没用啊,浪费的问题有其他办法解决,但不是在这个层面去解决,而是由 slab 分配器解决,这里不重点说后续会专门讲 slab 分配器是如何解决这个问题的.

第二:合并要求太严格了,一定得是伙伴 (连续) 才能合并成更大的块。这样也会导致时间久了很难有大块的连续性的猪肉块.

比方打完了,鸿蒙内核是如何实现卖肉算法的呢? 请看代码

LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
{struct VmFreeList *list = NULL;LosVmPage *page = NULL;UINT32 order;UINT32 newOrder;if ((seg == NULL) || (nPages == 0)) {return NULL;}//因为伙伴算法分配单元是 1,2,4,8 页,比如nPages = 3时,就需要从 4号空闲链表中分,剩余的1页需要劈开放到1号空闲链表中order = OsVmPagesToOrder(nPages);//根据页数计算出用哪个块组if (order < VM_LIST_ORDER_MAX) {//order不能大于9 即:256*4K = 1M 可理解为向内核堆申请内存一次不能超过1Mfor (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) {//没有就找更大块list = &seg->freeList[newOrder];//从最合适的块处开始找if (LOS_ListEmpty(&list->node)) {//理想情况链表为空,说明没找到continue;//继续找更大块的}page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);//找第一个节点就行,因为链表上挂的都是同样大小物理页框goto DONE;}}return NULL;
DONE:OsVmPhysFreeListDelUnsafe(page);//将物理页框从链表上摘出来OsVmPhysPagesSpiltUnsafe(page, order, newOrder);//将物理页框劈开,把用不了的页再挂到对应的空闲链表上return page;
}/******************************************************************************本函数很像卖猪肉的,拿一大块肉剁,先把多余的放回到小块肉堆里去.oldOrder:原本要买 2^2肉newOrder:却找到个 2^8肉块
******************************************************************************/
STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
{UINT32 order;LosVmPage *buddyPage = NULL;for (order = newOrder; order > oldOrder;) {//把肉剁碎的过程,把多余的肉块切成2^7,2^6...标准块,order--;//越切越小,逐一挂到对应的空闲链表上buddyPage = &page[VM_ORDER_TO_PAGES(order)];//@note_good 先把多余的肉割出来,这句代码很赞!因为LosVmPage本身是在一个大数组上,page[nPages]可直接定位LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);//没挂到伙伴算法对应组块空闲链表上的物理页框的order必须是VM_LIST_ORDER_MAXOsVmPhysFreeListAddUnsafe(buddyPage, order);//将劈开的节点挂到对应序号的链表上,buddyPage->order = order}
}

为了方便理解代码细节, 这里说一种情况: 比如三哥要买 3 斤的,发现 4 斤,8 斤的都没有了,只有 16 斤的怎么办? 注意不会给 16 斤,只会给 4 斤。这时需要把肉劈开,劈成 8,4,4,其中 4 斤给张三哥,将剩下的 8 斤,4 斤挂到对应链表上. OsVmPhysPagesSpiltUnsafe 干的就是劈猪肉的活.

伙伴算法的链表是怎么初始化的,再看段代码

//初始化空闲链表,分配物理页框使用伙伴算法
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
{int i;UINT32 intSave;struct VmFreeList *list = NULL;LOS_SpinInit(&seg->freeListLock);//初始化用于分配的自旋锁LOS_SpinLockSave(&seg->freeListLock, &intSave);for (i = 0; i < VM_LIST_ORDER_MAX; i++) {//遍历伙伴算法空闲块组链表list = &seg->freeList[i]; //一个个来LOS_ListInit(&list->node); //LosVmPage.node将挂到list->node上list->listCnt = 0;   //链表上的数量默认0}LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

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

开发基础知识:https://qr21.cn/FV7h05

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

基于ArkTS 开发:https://qr21.cn/FV7h05

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

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/810262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue】ref 和 reactive 对比

ref&#xff1a;存储单个数据&#xff0c;如数值&#xff0c;字符串reactive&#xff1a;存储复杂数据&#xff0c;如对象&#xff0c;数组 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"vie…

创建vue3项目遇到的部分问题及解决方式

export default defineConfig({server: {// https: true,// 启用https协议&#xff0c;默认是HTTP协议host: true, // 监听所有地址port: 1314,// 前端项目启动的端口号open: true,// 开启代理cors: false, // 为开发服务器配置 CORSproxy: {^/api: {target: ,// 服务器地址cha…

(Java)数据结构——图(第七节)Folyd实现多源最短路径

前言 本博客是博主用于复习数据结构以及算法的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 Folyd实现原理 中心点的概念 感觉像是充当一个桥梁的作用 还是这个图 我们常在一些讲解视频中看到&#xff0c;就比如dist&#xff08;-1&#xff09;&#xff0…

WooCommerce的使用方式和特点

WooCommerce是一款基于WordPress的开源电子商务插件&#xff0c;它允许用户在WordPress网站上创建和管理在线商店。以下是WooCommerce的一些使用方式和特点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.产品管理&#xff1a;Woo…

bugku-web-file_get_contents

<?php extract($_GET); if (!empty($ac)){$f trim(file_get_contents($fn));if ($ac $f){echo "<p>This is flag:" ." $flag</p>";}else{echo "<p>sorry!</p>";} } ?> 这里涉及到几个不常用的函数 这里直接构…

2024年 Mathorcup高校数学建模竞赛(B题)| 甲骨文识别 | 特征提取,图像分割解析,小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮200人完成了建模与思路的构建的处理了&#xff5e; 本篇文章是鹿鹿学长经过深度思考&#xff0c;独辟蹊径&#xff0c;通过神经网络解决甲骨文识别问题。结合特征提取&#xff0c;图像分割等多元算法&…

关于Ansible的模块 ⑦

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 在继《关于Ansible的模块 ①》、《关于Ansible的模块 ②》、《关于Ansible的模块 ③》与《关于Ansible的模块 ④》、《关于ansib…

python基础——python包【创建和导入,下载第三方包】

&#x1f4dd;前言&#xff1a; 在上一篇文章python基础——模块中&#xff0c;我们讲解了有关python模块的相关知识&#xff0c;这篇文章我们进一步讲解用于储存多个模块文件的python包&#xff1a; 1&#xff0c;什么是python包 2&#xff0c;如何创建和导入python包 3&#…

选择排序解读

在计算机科学中&#xff0c;排序算法是一种将数据元素按照某种顺序排列的算法。今天&#xff0c;我们要探讨的是选择排序&#xff08;Selection Sort&#xff09;&#xff0c;这是一种简单直观的排序方法&#xff0c;通过不断选择剩余元素中的最小&#xff08;或最大&#xff0…

SpringBoot + Dobbo + nacos

SpringBoot Dobbo nacos 一、nacos https://nacos.io/zh-cn/docs/quick-start.html 1、下载安装包 https://github.com/alibaba/nacos/releases/下载后在主目录下&#xff0c;创建一个logs的文件夹&#xff1a;用来存日志 2、启动nacos 在bin目录下打开cmd运行启动命令&a…

2024年DeFi的四大主导趋势:Restaking、Layer3、AI和DePin

DeFi&#xff08;去中心化金融&#xff09;行业在2024年将继续呈现快速增长的势头&#xff0c;驱动这一增长的主要因素将是四大主导趋势&#xff1a;Restaking、Layer3、AI和DePin。这些趋势将推动DeFi生态系统的发展&#xff0c;为用户提供更多的机会和创新。 趋势1&#xff…

ERA拓展之旅:2024香港Web3峰会聚焦全球Web3发展

2024年香港Web3大型峰会是一次令人难忘的体验。这次峰会吸引了来自世界各地的Web3爱好者和从业者齐聚一堂&#xff0c;共同探讨着Web3的未来发展方向与机遇。在这个热情洋溢的氛围中&#xff0c;展现了对Web3的热情&#xff0c;分享彼此的见解和理念&#xff0c;探讨了未来的行…

性能优化-01

当看到性能指标时&#xff0c;你会首先想到什么呢&#xff1f;我相信 “高并发” 和 “响应快” 一定是最先出现在你脑海里的两个词&#xff0c;而它们也正对应着性能优化的两个核心指标—— “吞吐” 和 “延时” 。这两个指标是从应用负载的视角来考察性能&#xff0c;直接影…

Chatgpt掘金之旅—有爱AI商业实战篇|SEO 咨询业务|(十七)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业在SEO 咨询业务有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随…

springboot+vue高校贫困生资助勤工俭学管理系统python

本次开发一套贫困生资助管理系统有管理员和用户两个角色。管理员功能有个人中心&#xff0c;学生管理&#xff0c;院校管理&#xff0c;贫困申请管理&#xff0c;资助项目管理&#xff0c;资助申请管理&#xff0c;资助发放管理&#xff0c;勤工俭学管理&#xff0c;岗位申请管…

CMake构建OpenCv并导入QT项目过程中出现的问题汇总

前言 再此之前请确保你的环境变量是否配置&#xff0c;这是总共需要配置的环境变量 E:\cmake\bin E:\OpenCv\opencv\build\x64\vc15\bin F:\Qt\Tools\mingw730_64\bin F:\Qt\5.12.4\mingw73_64\bin 问题一&#xff1a; CMake Error: CMake was unable to find a build program…

Linux网络 基础概念

目录 背景知识 互联网的发展 局域网和广域网 网络拓扑 网络协议栈 协议的概念 网络协议的分层 网络与操作系统的联系 网络传输的基本流程 IP地址和MAC地址 以太网通信 数据包的封装和分用 跨网段传输 背景知识 互联网的发展 计算机网络是计算机技术和通信技术相…

【Android Studio报错】:* What went wrong:Out of memory. Java heap space

项目场景&#xff1a; 今天&#xff0c;刚打开自己的安卓项目发现报错&#xff1a; 报错&#xff1a; * What went wrong: Out of memory. Java heap space Possible solution: - Check the JVM memory arguments defined for the gradle process in: gradle.properties in…

路由器端口映射是什么意思?

路由器端口映射是一种网络配置技术&#xff0c;在私有网络中允许外部网络访问特定的服务或应用程序。通过将路由器的端口映射到内部客户端设备&#xff0c;可以实现从公共网络访问内部网络资源的目的。 天联组网介绍 天联是一款异地组网内网穿透产品&#xff0c;由北京金万维科…

网工基础协议——IP地址

IP地址的概念&#xff1a; IP 地址是我们进行TCP/IP通讯的基础&#xff0c;每个连接到网络上的计算机都必须有一个!P地址。我们目前使用的IP地址是32位的&#xff0c;通常以点分十进制表示。例如:192.168.0.181。IP地址的格式为: IP地址 网络地址 主机地址 或者 |P地址主机地…