您现在的位置是:亿华云 > 数据库
鸿蒙轻内核A核源码分析系列之虚实映射(3)虚拟物理内存映射
亿华云2025-10-08 20:56:52【数据库】5人已围观
简介想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com3、虚实映射函数LOS_ArchMmuMap从上文可知,用户程序加载启动时,会将代码段、数
想了解更多内容,鸿蒙核A核源请访问:
和华为官方合作共建的轻内鸿蒙技术社区
https://harmonyos.51cto.com
3、虚实映射函数LOS_ArchMmuMap
从上文可知,码分用户程序加载启动时,析系虚实虚拟会将代码段、映射映射数据段映射进虚拟内存空间,物理此时并没有物理页做实际的内存映射;程序执行时,如下图(图片来自OpenHarmony docs开源站点)粗箭头所示,鸿蒙核A核源CPU访问虚拟地址,轻内通过MMU查找是码分否有对应的物理内存,若该虚拟地址无对应的析系虚实虚拟物理地址则触发缺页异常,内核申请物理内存并将虚实映射关系及对应的映射映射属性配置信息写进页表,并把页表条目缓存至TLB,物理接着CPU可直接通过转换关系访问实际的内存物理内存;若CPU访问已缓存至TLB的页表条目,无需再访问保存在内存中的鸿蒙核A核源页表,可加快查找速度。本小节我们就详细分析下虚实映射函数的实现代码。

3.1 函数LOS_ArchMmuMap
函数LOS_ArchMmuMap用于映射进程空间虚拟地址区间与物理地址区间,其中输入参数archMmu为MMU结构体,vaddr和paddr分别是虚拟内存和物理内存的开始地址;count为虚拟地址和物理地址映射的内存页数量;flags为映射标签。⑴处进行函数参数校验,b2b供应网不支持NON-SECURE的标记,虚拟地址和物理地址需要内存页4KiB对齐,参数检查函数代码简单,自行查看即可。⑵处当虚拟地址、物理地址基于1MiB对齐,并且数量count大于256时,使用Section页表项格式。⑶处生成L1 section类型页表项并保存,下文详细分析该函数OsMapSection()。如果不满足⑵处条件,需要使用L2映射。首先执行⑷处获取虚拟地址vaddr对应的L1页表项,接着执行⑸处判断是否映射,如果没有对应的映射,则执行⑹处的函数OsMapL1PTE生成L1 page table类型页表项并保存,然后执行函数OsMapL2PageContinous生成L2 页表项并保存。如果已经映射为L1 page table页表项类型,则执行函数OsMapL2PageContinous生成L2 页表项并保存。如果不是支持的页表项类型,则执行LOS_Panic()触发异常。⑺处统计生成映射的调试,最终会返回映射成功的数量。
可以看出,b2b信息网在给定虚实内存地址和映射的内存页数后,使用L1页表映射还是L2页表映射的判断条件是:虚实内存地址是否1MiB内存对齐,并且映射数量是否大于256。当使用L1映射时,映射为Section页表项类型。当使用L2映射时,根据L1页表项类型,分布处理无效页表项和Page Table页表项类型这2种情况。具体的映射方式见下文。
status_t LOS_ArchMmuMap(LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr, size_t count, UINT32 flags) { PTE_T l1Entry; UINT32 saveCounts = 0; INT32 mapped = 0; INT32 checkRst; ⑴ checkRst = OsMapParamCheck(flags, vaddr, paddr); if (checkRst < 0) { return checkRst; } /* see what kind of mapping we can use */ while (count > 0) { ⑵ if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) && MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(paddr) && count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) { /* compute the arch flags for L1 sections cache, r ,w ,x, domain and type */ ⑶ saveCounts = OsMapSection(archMmu, flags, &vaddr, &paddr, &count); } else { /* have to use a L2 mapping, we only allocate 4KB for L1, support 0 ~ 1GB */ ⑷ l1Entry = OsGetPte1(archMmu->virtTtb, vaddr); ⑸ if (OsIsPte1Invalid(l1Entry)) { ⑹ OsMapL1PTE(archMmu, &l1Entry, vaddr, flags); saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count); } else if (OsIsPte1PageTable(l1Entry)) { saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count); } else { LOS_Panic("%s %d, unimplemented tt_entry %x/n", __FUNCTION__, __LINE__, l1Entry); } } ⑺ mapped += saveCounts; } return mapped; }3.2 OsMapSectionL1 Section类型页表项映射函数
函数OsMapSection生成L1 Section类型页表项并保存。⑴处把内存区间标签(这些标签定义在文件kernel\base\include\los_vm_map.h中,标签名称一般为VM_MAP_REGION_FLAG_XXXX)转换为MMU标签(定义在arch\arm\arm\include\los_mmu_descriptor_v6.h中,标签名称一般为MMU_DESCRIPTOR_L1_TYPE_XXXX)。 ⑵处的函数OsGetPte1Ptr(archMmu->virtTtb, *vaddr)用于获取虚拟地址对应的页表项索引地址,等于页表项基地址加上虚拟地址的高20位;OsTruncPte1(*paddr) | mmuFlags | MMU_DESCRIPTOR_L1_TYPE_SECTION)为物理内存地址的高12位+MMU标签+页表项Section类型值。该行语句的作用是把虚拟地址和物理地理进行映射,映射关系维护在页表项。这行代码比较关键,我们绘制下图形来表示,见下图。源码下载⑶处把虚拟地址和物理地址增加1MiB的大小,映射数量减去256(1MiB有256个4KiB大小的内存页)。
3.3 函数OsGetL2Table生成L2页表项基地址
函数OsGetL2Table用于生成L2页表,函数参数中archMmu是MMU结构体,l1Index是L1页表项索引(页号),ppa属于输出参数,保存L2页表项基地址。⑴处计算L2页表项偏移值(为啥这么计算? 看不懂 TODO),其中(MMU_DESCRIPTOR_L2_SMALL_SIZE / MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE)的大小等于1024;l1Index & (MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE - 1)为虚拟地址的第20-21位。⑵处通过循环遍历查询是否存在L2页表(为啥查询4次?TODO),⑶处获取页表项基地址,然后判断是否页表类型,如果是,则返回L2页表项基地址。
如果没有存在的页表,则为L2页表申请内存,如果支持虚拟地址LOSCFG_KERNEL_VM,执行⑷使用LOS_PhysPageAlloc申请内存页,把申请的内存页挂载页表链表上,并根据内存页计算虚拟内存地址kvaddr;如果不支持虚拟地址,执行⑸使用LOS_MemAlloc申请内存。⑹处转换为物理地址,然后加上页表偏移值l2Offset返回L2页表项基地址。
STATIC STATUS_T OsGetL2Table(LosArchMmu *archMmu, UINT32 l1Index, paddr_t *ppa) { UINT32 index; PTE_T ttEntry; VADDR_T *kvaddr = NULL; ⑴ UINT32 l2Offset = (MMU_DESCRIPTOR_L2_SMALL_SIZE / MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE) * (l1Index & (MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE - 1)); /* lookup an existing l2 page table */ ⑵ for (index = 0; index < MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE; index++) { ⑶ ttEntry = archMmu->virtTtb[ROUNDDOWN(l1Index, MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE) + index]; if ((ttEntry & MMU_DESCRIPTOR_L1_TYPE_MASK) == MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE) { *ppa = (PADDR_T)ROUNDDOWN(MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(ttEntry), MMU_DESCRIPTOR_L2_SMALL_SIZE) + l2Offset; return LOS_OK; } } #ifdef LOSCFG_KERNEL_VM /* not found: allocate one (paddr) */ ⑷ LosVmPage *vmPage = LOS_PhysPageAlloc(); if (vmPage == NULL) { VM_ERR("have no memory to save l2 page"); return LOS_ERRNO_VM_NO_MEMORY; } LOS_ListAdd(&archMmu->ptList, &vmPage->node); kvaddr = OsVmPageToVaddr(vmPage); #else ⑸ kvaddr = LOS_MemAlloc(OS_SYS_MEM_ADDR, MMU_DESCRIPTOR_L2_SMALL_SIZE); if (kvaddr == NULL) { VM_ERR("have no memory to save l2 page"); return LOS_ERRNO_VM_NO_MEMORY; } #endif (VOID)memset_s(kvaddr, MMU_DESCRIPTOR_L2_SMALL_SIZE, 0, MMU_DESCRIPTOR_L2_SMALL_SIZE); /* get physical address */ ⑹ *ppa = LOS_PaddrQuery(kvaddr) + l2Offset; return LOS_OK; }3.4 OsMapL1PTEL1 Page Table类型页表项映射函数
和函数OsMapSection对应,函数OsMapL1PTE用于生成L1 Page Table类型页表项并保存,其中函数参数pte1Ptr是L1页表项地址。⑴处调用函数OsGetL2Table()获取L2页表项基地址TTB,⑵处把L2页表项基地址加上描述符类型作为L1页表项数据。⑶处开始的3行代码为页表项设置标签,⑷处为虚拟内存地址vaddr保存页表项数据。
STATIC VOID OsMapL1PTE(LosArchMmu *archMmu, PTE_T *pte1Ptr, vaddr_t vaddr, UINT32 flags) { paddr_t pte2Base = 0; ⑴ if (OsGetL2Table(archMmu, OsGetPte1Index(vaddr), &pte2Base) != LOS_OK) { LOS_Panic("%s %d, failed to allocate pagetable\n", __FUNCTION__, __LINE__); } ⑵ *pte1Ptr = pte2Base | MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE; ⑶ if (flags & VM_MAP_REGION_FLAG_NS) { *pte1Ptr |= MMU_DESCRIPTOR_L1_PAGETABLE_NON_SECURE; } *pte1Ptr &= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_MASK; *pte1Ptr |= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_CLIENT; // use client AP ⑷ OsSavePte1(OsGetPte1Ptr(archMmu->virtTtb, vaddr), *pte1Ptr); }3.5 OsMapL2PageContinuous映射L2页表函数
函数OsMapL2PageContinuous用于映射L2页表项,其中函数参数pte1为L1页表项数据,flags为虚实映射标签,vaddr为虚拟内存,paddr为物理内存,count为需要映射的内存页数量。
⑴处根据L1页表项数据获取L2页表项虚拟内存基地址。页表项的高22位为L2页表项的物理内存基地址,然后转换为虚拟内存基地址即可。⑵处把地址区间标签转换为L2页表标签,⑶处连续设置L2页表项数据,saveCounts表示映射了多少个L2页表项。⑷处映射L2页表项数据后,更新虚拟、物理内存地址,更新映射后的内存页数量count。由于一个L2页表项占用4KiB大小,saveCounts个页表项,需要把saveCounts左移12位来增长内存地址。
STATIC UINT32 OsMapL2PageContinuous(PTE_T pte1, UINT32 flags, VADDR_T *vaddr, PADDR_T *paddr, UINT32 *count) { PTE_T *pte2BasePtr = NULL; UINT32 archFlags; UINT32 saveCounts; ⑴ pte2BasePtr = OsGetPte2BasePtr(pte1); if (pte2BasePtr == NULL) { LOS_Panic("%s %d, pte1 %#x error\n", __FUNCTION__, __LINE__, pte1); } /* compute the arch flags for L2 4K pages */ ⑵ archFlags = OsCvtPte2FlagsToAttrs(flags); ⑶ saveCounts = OsSavePte2Continuous(pte2BasePtr, OsGetPte2Index(*vaddr), *paddr | archFlags, *count); ⑷ *paddr += (saveCounts << MMU_DESCRIPTOR_L2_SMALL_SHIFT); *vaddr += (saveCounts << MMU_DESCRIPTOR_L2_SMALL_SHIFT); *count -= saveCounts; return saveCounts; }想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com
很赞哦!(5864)
相关文章
- 为什么喜欢国外注册域名?国外注册域名注意什么?
- 企业邮箱域名含义-企业邮箱域名申请
- 免费的虚拟主机和域名值得信赖吗?
- ro域名是什么域名?小白可以注册吗?
- 前面这两个步骤都是在本机完成的。到这里还没有涉及真正的域名解析服务器,如果在本机中仍然无法完成域名的解析,就会真正请求域名服务器来解析这个域名了。
- Spring框架之Spring AOP Logging教程
- 2021年11月22日便宜域名推荐
- 我怎么在 Spring Boot 中使用 JDBC 连接 MySQL
- 只要我们做的是从目前的市场情况选择域名,从简单易记,从个性特征上,我们就可以找到一个好域名进行注册。域名注册进行域名记录和解析以及绑定网站后,客户可以通过URL登录您的网站。
- 基于HarmonyOS ArkUI 3.0框架,我成功开发了流式布局网络语
热门文章
站长推荐
个人域名转为公司需要什么条件?个人域名转为公司该怎么做?
域名租给别人价格高吗?出租域名好不好?
IOC-Golang 的 AOP 原理与应用
手把手带你用数据库中间件Mycat+SpringBoot完成分库分表
3、不明先知,根据相关征兆预测可能发生的事件,以便提前做好准备,赶紧注册相关域名。;不差钱域名;buchaqian抢先注册,就是这种敏感类型。预言是最敏感的状态。其次,你应该有眼力。所谓眼力,就是善于从社会上时不时出现的各种热点事件中获取与事件相关的域名资源。眼力的前提是对域名领域的熟悉和丰富的知识。
中国香港域名购买去哪里?价格高不高?
MySQL是如何进行排序的?怎么使用性能更快!
面试官:有了解过Volatile关键字吗 说说看