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

如何初始化物理内存?

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

#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.rhkb.cn/news/306076.html

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

相关文章

【网络初识】网络相关概念详解

一.局域网VS广域网 局域网 局域网:Local Area Network~简称LAN.指在某一特定区域内由多台计算机组成的互联网组。局域网内的主机之间能方便的进行网络通信&#xff0c;又称为内网.局域网和局域网之间在没有连接的情况下&#xff0c;是无法通信的。局域网的组建方式: 基于网线…

新规来了!智能音视频技术重塑信贷体验

近日&#xff0c;国家金融监督管理总局发布《固定资产贷款管理办法》《流动资金贷款管理办法》《个人贷款管理办法》&#xff08;以下简称“三个办法”&#xff09;。 具体来看&#xff0c;新规明确了以下要求&#xff1a; 1、明确视频面签、电子签约要求 允许商业银行通过视…

【话题】AI技术创业有那些机会,简单探讨下

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 背景机会一、引言二、AI技术的创业机遇1.智能服务行业的兴起2.数据驱动的业务模式创新3.AI与产业融合的创新发展 三、AI技术创业的挑战1.技术门槛高2.法规政策的不确定性…

Java 中文官方教程 2022 版(三十四)

原文&#xff1a;docs.oracle.com/javase/tutorial/reallybigindex.html 长期持久性 原文&#xff1a;docs.oracle.com/javase/tutorial/javabeans/advanced/longpersistence.html 长期持久性是一种模型&#xff0c;可以将 bean 保存为 XML 格式。 有关 XML 格式和如何为非 be…

软件定义网络中的网络流量控制

软件定义网络&#xff08;SDN&#xff09;是一种网络范例&#xff0c;它支持一种新的方法来构建网络基础设施&#xff0c;其主要概念是基于集中控制平面和分布式转发平面的分离&#xff0c;其中整个网络及其设备通过使用开放API的软件应用程序进行集中控制和管理。SDN使网络虚拟…

Java高频面试之JVM篇

说一下 Jvm 的主要组成部分&#xff1f;及其作用&#xff1f; 类加载器执行器运行时数据区域本地接口 谈谈对运行时数据区的理解&#xff1f; 堆和栈的区别是什么&#xff1f; 堆中存什么&#xff1f;栈中存什么&#xff1f; 堆总存对象,栈中存局部变量,引用 为什么要把堆…

设计模式系列:单例模式

作者持续关注WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;WPS二次开发QQ群:250325397&#xff09;&#xff0c;摸鱼吹牛嗨起来&#xff01…

一起学习python——基础篇(15)

今天讲一下python的网络请求方法。 如果使用python语言做接口测试&#xff0c;可以安装Requests模块。 安装步骤&#xff1a;打开编程软件&#xff0c;然后点击settings——>Project:System——>Python Interpreter——>加号——>搜索栏里面输入“Requests”——&…

Redis中的集群(一)

集群 概述 Redis集群是Redis提供的分布式数据库方案&#xff0c;集群通过分片(sharding)来进行数据共享&#xff0c;并提供复制和故障转移功能 节点 一个Redis集群通常由多个节点(node)组成&#xff0c;在刚开始的时候&#xff0c;每个节点都是相互独立的&#xff0c;它们都…

appium

app元素抓取在线工具 Appium Inspector by Appium Pro appium安装&#xff08;通过node.js安装&#xff09; Python3Appium安装使用教程_python_脚本之家 Node version is 18.17.1

[计算机网络] 当输入网址到网页

HTTP 首先&#xff0c;对URL进行解析&#xff0c;URL包含了Web服务器和对应的文件&#xff08;文件路径&#xff09; URL是请求服务器中的文件资源 通过Web服务器和对应文件来生产HTTP包&#xff08;超文本传输协议&#xff09; DNS 根据域名查询对应的IP地址 域名的层级 根…

代理模式:控制对象访问的智能方式

在面向对象的软件开发中&#xff0c;代理模式是一种结构型设计模式&#xff0c;它为其他对象提供一个代理或占位符以控制对这个对象的访问。代理模式在实现权限控制、延迟初始化和远程对象访问等方面非常有用。本文将详细介绍代理模式的定义、实现、应用场景以及优缺点&#xf…

奎芯科技:智能时代的芯片上游企业如何突破?

半导体IP&#xff08;Intellectual Property&#xff0c;知识产权&#xff09;&#xff0c;通常也称作IP核&#xff08;IP core&#xff09;&#xff0c;指芯片设计中预先设计、验证好的功能模块&#xff0c;主要服务于芯片设计&#xff0c;因部分通用功能模块在芯片中被反复使…

鸿蒙南向开发:【智能烟感】

样例简介 智能烟感系统通过实时监测环境中烟雾浓度&#xff0c;当烟雾浓度超标时&#xff0c;及时向用户发出警报。在连接网络后&#xff0c;配合数字管家应用&#xff0c;用户可以远程配置智能烟感系统的报警阈值&#xff0c;远程接收智能烟感系统报警信息。实现对危险及时报…

智慧港口多场景解决方案(二)

港机智能化管理 港机智能化管理解决方案 [功能特点」 专门定制适合港机应用的专业摄像机高性能无线传输系统,高带宽、高抗干扰能力、高频谱利用率高移动性:支持港机移动高清视频的实时传送多业务承载能力:支持视频图像、CMS、告警数据、生产数据等业务的同时接入统一管理平台…

微软卡内基梅隆大学:无外部干预,GPT4等大语言模型难以自主探索

目录 引言&#xff1a;LLMs在强化学习中的探索能力探究 研究背景&#xff1a;LLMs的在情境中学习能力及其重要性 实验设计&#xff1a;多臂老虎机环境中的LLMs探索行为 实验结果概览&#xff1a;LLMs在探索任务中的普遍失败 成功案例分析&#xff1a;Gpt-4在特定配置下的探…

YOLOv8草莓生长状态(灰叶病缺钙需要肥料)检测系统(python开发,带有训练模型,可以重新训练,并有Pyqt5界面可视化)

本次检测系统&#xff0c;不仅可以检测图片、视频或摄像头当中出现的草莓叶子是否有灰叶病&#xff0c;还可以检测出草莓叶是否缺钙、是否需要施肥等状态。基于最新的YOLO-v8训练的草莓生长状态检测模型和完整的python代码以及草莓的训练数据&#xff0c;下载后即可运行&#x…

nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)

一、安装nvm 1. 下载nvm 点击 网盘下载 进行下载 2、双击下载好的 nvm-1.1.12-setup.zip 文件 3.双击 nvm-setup.exe 开始安装 4. 选择我接受&#xff0c;然后点击next 5.选择nvm安装路径&#xff0c;路径名称不要有空格&#xff0c;然后点击next 6.node.js安装路径&#…

【氮化镓】缓冲层结构对GaN HEMT射频性能的影响

【Effect of different layer structures on the RF performance of GaN HEMT devices】 研究总结&#xff1a; 本研究探讨了不同缓冲层结构对氮化镓高电子迁移率晶体管&#xff08;GaN HEMT&#xff09;射频性能的影响。通过对比三种不同缓冲层结构的GaN HEMT设备&#xff0…

【YOLOv9改进[损失函数]】使用MPDIou回归损失函数帮助YOLOv9模型更优秀

本文中&#xff0c;第一部分概述了各种回归损失函数&#xff0c;当然也包括了今天的主角MPDIou。第二部分内容为在YOLOv9中使用MPDIou回归损失函数的方法。 1 回归损失函数&#xff08;Bounding Box Regression Loss&#xff09; 边界框回归损失计算的方法包括GIoU、DIoU、CI…