鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除

2024-06-12 21:44

本文主要是介绍鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系,其中参数包含MMU结构体、解除映射的虚拟地址和解除映射的数量count,数量的单位是内存页数。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项数据。⑵处计算需要解除的无效映射的数量,后文再详细分析该函数。如果页表项映射类型为L1 Section,并且虚拟地址1MiB对齐,映射的数量超过256,则执行⑶解除映射Section,后文详细分析函数OsUnmapSection。如果页表项映射类型为Page Table,则执行⑷先解除二级页表映射,然后尝试解除一级页表映射,涉及的2个函数后文详细分析。从虚拟地址开始的需要接触映射的内存页中,可能部分是L2映射,部分是L1映射。完成L2映射后,需要判断是否存在L1映射,如果存在也需要解除映射。⑹处函数使TLB失效,涉及些cp15寄存器和汇编,后续再分析。

STATUS_T LOS_ArchMmuUnmap(LosArchMmu *archMmu, VADDR_T vaddr, size_t count)
{PTE_T l1Entry;INT32 unmapped = 0;UINT32 unmapCount = 0;while (count > 0) {
⑴      l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);if (OsIsPte1Invalid(l1Entry)) {
⑵          unmapCount = OsUnmapL1Invalid(&vaddr, &count);} else if (OsIsPte1Section(l1Entry)) {if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) && count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) {
⑶              unmapCount = OsUnmapSection(archMmu, &vaddr, &count);} else {LOS_Panic("%s %d, unimplemented\n", __FUNCTION__, __LINE__);}} else if (OsIsPte1PageTable(l1Entry)) {
⑷          unmapCount = OsUnmapL2PTE(archMmu, vaddr, &count);OsTryUnmapL1PTE(archMmu, vaddr, OsGetPte2Index(vaddr) + unmapCount,MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - unmapCount);
⑸          vaddr += unmapCount << MMU_DESCRIPTOR_L2_SMALL_SHIFT;} else {LOS_Panic("%s %d, unimplemented\n", __FUNCTION__, __LINE__);}unmapped += unmapCount;}
⑹  OsArmInvalidateTlbBarrier();return unmapped;
}

5.1 函数OsUnmapL1Invalid

函数OsUnmapL1Invalid用于解除无效的映射,会把虚拟地址增加,映射的数量减少。⑴处的MMU_DESCRIPTOR_L1_SMALL_SIZE表示1MiB大小,*vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE对1MiB取余,MMU_DESCRIPTOR_L1_SMALL_SIZE - (*vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE)表示1MiB大小的内存中分为2部分,一部分在虚拟地址vaddr前,一部分在虚拟地址后,这里取虚拟地址之后的部分。然后向右偏移12位>>MMU_DESCRIPTOR_L2_SMALL_SHIFT转换为内存页数量,再取内存页数的较小的数值。⑵处把解除映射的内存页数量左移12位转换为地址长度,然后更新虚拟地址。⑶处减去已经解除映射的数量。

STATIC INLINE UINT32 OsUnmapL1Invalid(vaddr_t *vaddr, UINT32 *count)
{UINT32 unmapCount;⑴  unmapCount = MIN2((MMU_DESCRIPTOR_L1_SMALL_SIZE - (*vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE)) >>MMU_DESCRIPTOR_L2_SMALL_SHIFT, *count);
⑵  *vaddr += unmapCount << MMU_DESCRIPTOR_L2_SMALL_SHIFT;
⑶  *count -= unmapCount;return unmapCount;
}

5.2 函数OsUnmapSection

函数OsUnmapSection用于解除一级页表的Section映射。⑴处把虚拟地址对应的页表项数据清除为0。⑵处使TLB寄存器失效,⑶更新虚拟地址和映射数量,虚拟地址增加1MiB大小,映射数量减去256。

STATIC UINT32 OsUnmapSection(LosArchMmu *archMmu, vaddr_t *vaddr, UINT32 *count)
{
⑴  OsClearPte1(OsGetPte1Ptr((PTE_T *)archMmu->virtTtb, *vaddr));
⑵  OsArmInvalidateTlbMvaNoBarrier(*vaddr);⑶  *vaddr += MMU_DESCRIPTOR_L1_SMALL_SIZE;*count -= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;return MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;
}

5.3 函数OsUnmapL2PTE

函数OsUnmapL2PTE用于解除L2页表映射。⑴处先调用函数OsGetPte1()计算虚拟内存地址对应的L1页表项,然后调用函数OsGetPte2BasePtr()计算虚拟地址对应的L2页表基地址。⑵处获取虚拟地址对应的的L2页表项索引,计算方式上文已经讲述。⑶处计算需要解除映射的内存页数量,MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - pte2Index表示虚拟内存地址对应的能解除映射的最大数量,使用该值与count取最小值。⑷处依次解除各个二级页表的映射,把对应的各个L2页表项设置为0。⑸处使TLB缓存失效。

STATIC UINT32 OsUnmapL2PTE(const LosArchMmu *archMmu, vaddr_t vaddr, UINT32 *count)
{UINT32 unmapCount;UINT32 pte2Index;PTE_T *pte2BasePtr = NULL;⑴  pte2BasePtr = OsGetPte2BasePtr(OsGetPte1((PTE_T *)archMmu->virtTtb, vaddr));if (pte2BasePtr == NULL) {LOS_Panic("%s %d, pte2 base ptr is NULL\n", __FUNCTION__, __LINE__);}⑵  pte2Index = OsGetPte2Index(vaddr);
⑶  unmapCount = MIN2(MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - pte2Index, *count);/* unmap page run */
⑷  OsClearPte2Continuous(&pte2BasePtr[pte2Index], unmapCount);/* invalidate tlb */
⑸  OsArmInvalidateTlbMvaRangeNoBarrier(vaddr, unmapCount);*count -= unmapCount;return unmapCount;
}

5.4 OsTryUnmapL1PTE函数

函数OsTryUnmapL1PTE()用于解除L1页表映射,其中参数需要MMU结构体、虚拟内存地址vaddr、页表项索引scanIndex和要解除映射的内存页数scanCount。调用该函数时,页表项索引传入参数scanIndex的实参为OsGetPte2Index(vaddr) + unmapCount,即虚拟内存对应的L2页表项索引加上解除映射的页数量;要解除映射的内存页数量参数的实参为MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - unmapCount,即256减去已经解除映射的数量。回忆上文调用该函数OsTryUnmapL1PTE()的代码处,先调用OsUnmapL2PTE()函数解除unmapCount个映射,然后调用该函数解除映射MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - unmapCount个映射。

⑴处先执行函数OsGetPte1(archMmu->virtTtb, vaddr)获取页表项,然后执行函数OsGetPte2BasePtr()获得L2页表项基地址。⑵处执行循环检测是否存在可以解除映射的页表映射。⑶当scanIndex等于256时,置为0。⑷处当L2页表项不为0时,此时存在L2页表映射,跳出while循环。⑸处页数减1,不为0时则继续while循环。

当可以解除映射的数量为0时,执行⑹处代码,先获取L1页表项索引l1Index,然后获取对应的页表项l1Entry。执行⑺清零页表项,然后清理TLB缓存。⑻处调用函数OsPutL2Table()释放L2页表项内存,其中第3个实际参数MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(l1Entry)是L2y页表物理基地址。下面会详细看下函数的代码。

STATIC VOID OsTryUnmapL1PTE(const LosArchMmu *archMmu, vaddr_t vaddr, UINT32 scanIndex, UINT32 scanCount)
{/** Check if all pages related to this l1 entry are deallocated.* We only need to check pages that we did not clear above starting* from scanIndex and wrapped around SECTION.*/UINT32 l1Index;PTE_T l1Entry;PTE_T *pte2BasePtr = NULL;⑴  pte2BasePtr = OsGetPte2BasePtr(OsGetPte1(archMmu->virtTtb, vaddr));if (pte2BasePtr == NULL) {VM_ERR("pte2 base ptr is NULL");return;}⑵  while (scanCount) {
⑶      if (scanIndex == MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) {scanIndex = 0;}
⑷      if (pte2BasePtr[scanIndex++]) {break;}
⑸      scanCount--;}⑹  if (!scanCount) {l1Index = OsGetPte1Index(vaddr);l1Entry = archMmu->virtTtb[l1Index];/* we can kill l1 entry */
⑺      OsClearPte1(&archMmu->virtTtb[l1Index]);OsArmInvalidateTlbMvaNoBarrier(l1Index << MMU_DESCRIPTOR_L1_SMALL_SHIFT);/* try to free l2 page itself */
⑻      OsPutL2Table(archMmu, l1Index, MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(l1Entry));}
}

看下函数OsPutL2Table()的实现。⑴处遍历检查是否存在有L1页表项指向此L2页表,如果存在则返回。否则,需要释放L2页表项占用的内存。如果开启虚拟内存,则执行⑵获取物理内存对应的内存页,然后释放内存页。如果没有开启虚拟内存,则执行⑶调用函数LOS_MemFree()释放物理内存。

STATIC VOID OsPutL2Table(const LosArchMmu *archMmu, UINT32 l1Index, paddr_t l2Paddr)
{UINT32 index;PTE_T ttEntry;/* check if any l1 entry points to this l2 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) {return;}}
#ifdef LOSCFG_KERNEL_VM/* we can free this l2 table */
⑵  LosVmPage *vmPage = LOS_VmPageGet(l2Paddr);if (vmPage == NULL) {LOS_Panic("bad page table paddr %#x\n", l2Paddr);return;}LOS_ListDelete(&vmPage->node);LOS_PhysPageFree(vmPage);
#else
⑶  (VOID)LOS_MemFree(OS_SYS_MEM_ADDR, LOS_PaddrToKVaddr(l2Paddr));
#endif
}

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:https://qr21.cn/FV7h05

这篇关于鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1055424

相关文章

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

Kotlin Map映射转换问题小结

《KotlinMap映射转换问题小结》文章介绍了Kotlin集合转换的多种方法,包括map(一对一转换)、mapIndexed(带索引)、mapNotNull(过滤null)、mapKeys/map... 目录Kotlin 集合转换:map、mapIndexed、mapNotNull、mapKeys、map

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原