内存管理篇-17解开页表的神秘面纱-下

news2024/11/13 9:05:02

1.页表初探遗留问题-页表的创建过程

  • 使用MMU之前,页表要准备好,怎么准备的?如何把物理内存通过section映射构建页表
  • 页表的创建过程分析:__create_page_tables--创建临时页表,然后在开启MMU
    • 页表的大小和用途
    • 页表在内存中的地址
    • 页表的创建过程
  • 内核在上电的时候,MMU还没有开启,此时运行在物理内存(前期都是一些汇编指令,这些指令和相对地址无关)。C语言的函数都是编译链接成虚拟地址,所以需要尽快打开MMU(在汇编阶段)。然后,在开启MMU之前,页表需要准备好。
  • 处理器工作在实地址模式(real address mode)或物理地址模式(physical address mode)。在这种模式下,所有的内存访问都是直接基于物理地址的,这意味着CPU直接将程序中的内存地址解释为物理内存中的地址。在Linux内核启动的早期阶段,它也运行在实地址模式下。在这个阶段,内核需要初始化MMU并设置页表,以便能够切换到保护模式(protected mode),此时MMU开始工作,可以支持虚拟内存。汇编语言或其他底层编程语言编写的代码可以直接操作物理地址,因为没有MMU来进行地址转换。一旦MMU被启用并且设置了适当的页表之后,操作系统就可以使用虚拟地址,这些地址会被MMU自动映射到物理地址。

2.构建section页表的示例

        背景:在特殊的场景下,配置的内核和用户是1:1,0x0 - 0x80000000-1是用户空间,0x80000000 - 0xffffffff是内核空间。并且dram上物理地址的起始是0x60000000 - xxxx。
        在Linux内核中,初始化阶段会使用一些特殊的页表条目来映射关键的内存区域。这些条目可能覆盖整个内存范围,或者特定的大块区域,例如内核映像本身和其他重要的内存段。例如,在x86架构上,Linux内核可能会使用一些特殊的宏和函数来设置这些大的映射。这通常发生在内核的初始化代码中,例如在arch/x86/kernel/head.S文件中,可以看到初始化MMU的相关代码。当MMU被完全启用后,内核会逐步细化这些大页映射,将其分解为更小的页表条目,以便提供更精细的内存管理控制。
(1)页表最开始一般放在(内核起始空间偏移0x4000)起始地址0x80000000偏移0x4000的这个地方。-原因是啥?0x80004000-0x80008000之间,大小是16KB。并且当前物理平台的物理地址是从0x60000000开始,虚拟地址是从0x80000000开始(内核配置了用户和内核地址比例为1:1)
(2)接下来我们需要把我们的代码段,数据段,bss段等区域的页表创建好,并且映射到物理内存。建立好页表之后才能开启MMU。

(3)早期的映射是采用section映射方式(一级页表映射),1M位单位进行映射。前面已经说到,1MB大页进行映射,4GB需要4096个一级页表的entry,每个entry占用4字节,一共16KB的大小。需要把这个内容存放在物理内存上。
(4)以一个虚拟地址为0x80108000虚拟地址为例,转成物理地址。首先去掉后20位(1MB大页映射,页内偏移为 1MB)得到index为0x801,接着去page table找到第0x801项entry,(此时page table也是在内存上的一部分空间,是按照字节编址,但是每个单位是4字节,因此第0x801项就是0x801*4字节=0x2004的物理地址所在的地方。) 找到对应的entry后,查看物理PFN为0x601,因为物理地址的编制是0x60000000,所以直接找到0x601 << 20 + 0x08000就得到物理地址了。

3.源码分析-将内核.text - .bss映射

(5)代码分析:D:\open_project\linux-5.10.84\linux-5.10.84\arch\arm\kernel\head.S汇编中的__create_page_tables函数就是进行准备页表的过程。

  • 在上电后不久就直接调用了 bl __create_page_tables函数;/*在ARM架构中,pgtbl 是一个伪指令,用于生成特定的内存映射条目,通常用于初始化页表。这个伪指令在编译时被展开成一系列实际的机器码指令,用于设置页表条目的值。pgtbl 通常用于在内核初始化阶段设置页表,以便正确地配置内存映射。pgtbl 伪指令的功能是将第二个寄存器(在你的例子中是 r8)的内容复制到第一个寄存器(在你的例子中是 r4)中,并且在复制过程中可能会进行一些特定的处理,如清零某些位。*/
/*
 * Setup the initial page tables.  We only setup the barest
 * amount which are required to get the kernel running, which
 * generally means mapping in the kernel code.
 *
 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
 *
 * Returns:
 *  r0, r3, r5-r7 corrupted
 *  r4 = physical page table address
 */
__create_page_tables:
	pgtbl	r4, r8				@ page table address R4保存了页表的物理起始地址
/*在ARM架构中,pgtbl 是一个伪指令,用于生成特定的内存映射条目,通常用于初始化页表。这个伪指令在编译时被展开成一系列实际的机器码指令,用于设置页表条目的值。pgtbl 通常用于在内核初始化阶段设置页表,以便正确地配置内存映射。pgtbl 伪指令的功能是将第二个寄存器(在你的例子中是 r8)的内容复制到第一个寄存器(在你的例子中是 r4)中,并且在复制过程中可能会进行一些特定的处理,如清零某些位。*/
	/*
	 * Clear the swapper page table
	 */
	mov	r0, r4  			   @r0是内核页表的起始地址0x80004000
	mov	r3, #0				   @对16KB的临时页表进行清零r3 = 0
	add	r6, r0, #PG_DIR_SIZE   @内核页表的结束地址保存在r6里面 r6=0x80004000 + 16KB=0x80008000
1:	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6                @测试r0是否等于r6,清空这段空间
	bne	1b

。。。。。。
。。。。。。

	/*
	 * Map our RAM from the start to the end of the kernel .bss section.
	 * 将起始地址到bss段整个空间映射到物理内存上
	 * (1)根据虚拟地址,找到页表中对应的entry
	 * (2)然后是填充entry项,需要根据要映射的物理地址(这个物理地址是可以随便写,保证地址在真实的ddr空间即可),
	      然后将物理地址右移20为,获取到section base addr,然后或上entry中后面的flags。base addr | flags; 
	      此时就构成了完成的一个页表entry。最后,就是把所有的entry写到对应的内存上。
	 */
	add	r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER) @r0保存了整个page table的起始地址,内核镜像的起始entry
	ldr	r6, =(_end - 1)										@内核镜像末尾的虚拟地址
	orr	r3, r8, r7  @R3 = phys_offset | MMU flags,即0x600 | flags。r7保存了flags,前面设置了。r8是物理地址起始地址,拼凑entry
	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)		@end of kernek image page table. 内核镜像的结束entry
    /*@store命令,将页表项写到r0地址上,一开始就是第一个entry,0x8000000对应的section物理地址0x699填充到页表项中,r0=r0+4*/
1:	str	r3, [r0], #1 << PMD_ORDER	
	add	r3, r3, #1 << SECTION_SHIFT
	cmp	r0, r6
	bls	1b

这段ARM汇编代码主要负责在系统启动阶段,将内核的虚拟地址空间映射到物理内存中,具体涉及从内核的开始地址一直到.bss段结束的整个范围。下面是对每条指令的详细中文解释:

1. add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)

        这条指令计算出整个页表的起始地址,即将被映射的内核镜像的第一个页表项的地址。PAGE_OFFSET是一个常量,表示虚拟地址空间的偏移量,SECTION_SHIFT和PMD_ORDER是与页表层级和粒度相关的常量。通过这一计算,我们得到的是内核镜像在虚拟地址空间中的起始位置。

2. ldr r6, =(_end - 1)

        加载内核镜像的最后一个地址(即_end符号所指向的地址减一)到寄存器r6中。这代表内核镜像在虚拟地址空间中的结束位置。

3. orr r3, r8, r7 

        使用逻辑或运算符将物理地址(存储在r8中)与MMU标志位(存储在r7中)合并,并将结果存入r3。这里的r8包含了要映射的物理地址起始点,而r7则包含了一组标志位,这些标志位控制着对物理内存的访问权限。

4. add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)

        计算内核镜像在页表中的结束位置。通过将r6(内核镜像的虚拟地址结束位置)向右移位(lsr),并加上r4(页目录的基地址),得到内核镜像在页表中的最后一个条目的地址。

5. str r3, [r0], #1 << PMD_ORDER

        这是一条循环内的指令,用于将r3中的值(即包含物理地址和标志位的页表项)存储到由r0指向的地址处。#1 << PMD_ORDER是一个偏移量,用于在每次迭代后更新r0,使其指向下一个页表项的位置。这是在初始化页表时实际写入页表项的过程。

6. add r3, r3, #1 << SECTION_SHIFT

        增加r3中的物理地址值,准备下一次循环时使用。#1 << SECTION_SHIFT表示增加的大小,通常对应于一个section的大小,确保每次循环都能正确地映射到下一个section的物理地址。

7. cmp r0, r6

        比较r0(当前处理的页表项地址)和r6(页表的结束地址)。这是为了检查是否已经到达了页表的末尾。

8. bls 1b

        如果r0小于等于r6(即还没有达到页表的末尾),则跳转回标号1b继续执行循环。否则,循环结束,页表初始化完成。

        总体而言,这段代码的主要功能是设置页表,以确保内核的虚拟地址可以正确地映射到物理内存中,从而支持后续的操作系统运行。

(6)对等映射,要保证开启MMU前后能平滑过渡

        如何实现虚拟地址和物理地址相同?比如上面说到的,dram的物理起始是0x60000000,而内核虚拟地址是从0x80000000,此时内核镜像区所在的位置大概是0x80008000 - bss_end的区间,如果想要映射到相等的物理地址,只需要在section table中的物理页帧改成0x800即可。(因为整个页表的填充都是用户自己构建的,理论上想映射到哪里都行)。如果dram的地址是在0x60000000 - 0x70000000,此时应该如何处理呢?

这段代码的目的是创建一个对等映射(identity mapping),以便在启用MMU之前能够访问特定的内存区域。
对等映射是指虚拟地址和物理地址相同的映射关系,这对于某些关键代码段(例如用于启用MMU的代码)
来说非常重要,因为它确保这些代码段即使在MMU尚未完全初始化的情况下也能正确执行。
	/*
	 * Create identity mapping to cater for __enable_mmu.
	 * This identity mapping will be removed by paging_init().
	 */
	adr	r0, __turn_mmu_on_loc
	ldmia	r0, {r3, r5, r6}
	sub	r0, r0, r3			@ virt->phys offset
	add	r5, r5, r0			@ phys __turn_mmu_on
	add	r6, r6, r0			@ phys __turn_mmu_on_end
	mov	r5, r5, lsr #SECTION_SHIFT
	mov	r6, r6, lsr #SECTION_SHIFT

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

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

相关文章

您应该使用哪个矢量数据库? 选择最适合您需求的数据库

导言 矢量数据库已成为存储非结构化和结构化数据表示并编制索引的首选。 这些表征被称为向量嵌入&#xff0c;由嵌入模型生成。 向量存储在利用深度学习模型&#xff08;尤其是大型语言模型&#xff09;的应用开发中发挥着至关重要的作用。 什么是矢量数据库&#xff1f; 在…

中国文化艺术孙溟展浅析《绛帖》

孙溟展浅析《绛帖》 《绛帖》是汇集摹刻众多名帖。北宋时潘师旦共摹刻二十卷&#xff0c;以《淳化阁帖》为基础有增加和删减&#xff0c;因为是在降州《今山西新降》摹刻的故此而得名。 《绛帖》 《绛帖》 《绛帖》 《绛帖》 据传潘师旦去世后&#xff0c;他的两个儿子各…

双硬盘双系统怎么装?双硬盘双系统安装教程(win7+win10双系统)

最近有很网友想双硬盘装双系统&#xff0c;考虑到如果哪块硬盘坏了我另一块硬盘还可以启动&#xff0c;以防万一硬盘坏了一块无法使用问题&#xff0c;今天小编给大家带来的双硬盘双系统方法教程。 相关推荐&#xff1a; 如何安装双系统?安装双系统详细步骤 双系统怎么设置启动…

个人旅游网(2.1)——使用阿里云在springboot项目中发送短信

文章目录 一、背景介绍二、详细步骤2.1、申请资质2.2、申请签名2.3、申请模板2.4、申请accessKey秘钥对2.5、SDK的使用[!]2.5.1、项目中导入依赖2.5.2、发短信的工具类 一、背景介绍 验证码发送背后的功能原理图&#xff1a; 想要在项目中实现上述发送验证码的功能&#xff0c…

操作系统面试真题总结(三)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 什么是进程&#xff1f; 在操作系统中&#xff0c;进程是指正在执…

8月31复盘日记

8月31复盘日记 前言今日感恩今日记录今日感悟今日名言 前言 昨天因为回到宿舍比较晚&#xff0c;所以来不及进行记录。   昨天早上五点多就醒了&#xff0c;可能是睡眠质量特别好&#xff0c;基本一倒头就是深度睡眠。所以我直接就起床去健身房运动&#xff0c;练了臀和核心&…

FastAPI+Vue3零基础开发ERP系统项目实战课 20240831上课笔记 路径参数

遗留的问题 3、FastAPI4、MySQL85、SQLModel&#xff0c;和FastAPI框架是同一个作者&#xff0c;两者结合比较好用&#xff0c;但是学习成本较高&#xff0c;使用难度稍大6、Vue37、Tailwindcss8、Ant Design Vue&#xff0c;UI组件库9、数据同步&#xff1a;有一个API&#x…

基于卷积神经网络的磨削平板类零件擦伤检测

基于卷积神经网络的磨削平板类零件擦伤检测 前言正文 前言 还记得读研那会儿刚学习完了卷积神经网络&#xff0c;初步学会了最基础的分类问题&#xff0c;当时也有点python基础&#xff0c;同时对TensorFlow也有点入门了。正好我的课题中有一类缺陷比较难以用传统方法识别判断&…

Vue60 插槽

插槽 作用&#xff1a;让父组件可以向子组件指定位置插入html结构&#xff0c;也是一种组件间通信的方式&#xff0c;适用于 父组件 > 子组件 。 分类&#xff1a;默认插槽、具名插槽、作用域插槽 使用方式&#xff1a; 默认插槽&#xff1a; 父组件中&#xff1a;<Cat…

聊聊 OceanBase 资源管理

OceanBase 是分布式多租户架构数据库&#xff0c;其分布式集群从资源角度看可以分成集群、Zone、OB Server、资源池和租户等几个层次。 今天我们从集群和资源两个层次梳理 OceanBase 资源管理相关的概念。 OceanBase 集群管理 OceanBase 集群包含若干个Zone&#xff0c;一个Z…

CRE6511KL 原边SOP7电源芯片

CRE6511KL 是一款内置高压 BJT 功率开关管,5W多模式原边控制的开关电源芯片。CRE6511KL较少的外围元器件、较低的系统成本设计出高性能的交直流转换开关电源。CRE6511KL 提供了极为全面和性能优异的智能化保护功能&#xff0c;包括逐周期过流保护、软启动、芯片过温保护、输出过…

Hot Chips 2024:博通(Broadcom)展示AI计算ASIC的光学连接

引言 在2024年的Hot Chips会议上&#xff0c;博通展示了其最新的AI计算专用集成电路&#xff08;ASIC&#xff09;&#xff0c;这款ASIC集成了光学连接技术。这一展示不仅体现了博通在定制AI加速器领域的领先地位&#xff0c;也预示着未来数据中心网络技术的一个重要发展方向。…

Ubuntu上qt使用SSH样式表

SSH样式表 如果学习过web的同学都知道&#xff0c;我们在学习HTML的时候会用到样式表&#xff0c;我们使用它来更改我们的颜色、大小、背景等等。上到后面&#xff0c;老师会说&#xff1a;我们如果在HTML文件中编辑太多的样式&#xff0c;就会让代码看起来非常的繁琐&#xf…

Obsidian个人知识库搭建流程

Obsidian的安装 Obsidian官网 个人博客搭建 xlog【使用xlog搭建个人博客Step by Step】 知乎【使用xlog搭建个人博客Step by Step】

实习的一点回顾Gradle

1 项目目录下执行 ./gradlew build&#xff0c;用.gradlew自动构建 首先有一个gradlew&#xff08;linux&#xff09;或者gradlew.bat&#xff08;win&#xff09;是用来检查java配置&#xff0c;环境配置之类的东西 然后去启动gradle/wrapper/gradle-wrapper.jar去检测本地gra…

【Google Play版】bilibili 3.19.2最新国际版(如何鉴别是否官方?)

相信玩 B 站的小伙伴也不少&#xff0c;国内版确实太臃肿了&#xff0c;已经“变质”了。 这是主界面&#xff0c;我没有做任何设置&#xff0c;刚装好就是这样&#xff1a; 摘自 Play 最新的评论&#xff1a; “3.19非常好&#xff0c;解决了我之前两个痛点。一是首页的自动…

基于PCA-SVM的人脸识别系统(MATLAB GUI)

matlab人脸识别系统(GUI),PCASVM方法 源码详细注释以及适配于本系统的5k字的理论资料&#xff08;方便学习&#xff09; GUI里的人脸识别算法&#xff1a;PCA-SVM 功能&#xff1a;利用ORL数据库&#xff0c;进行训练&#xff0c;测试&#xff0c;并可对单张图片进行识别 两个界…

ArrayList的详细使用教程

ArrayList实现了List接口&#xff0c;是顺序容器&#xff0c;即元素存放的数据与放进去的顺序相同&#xff0c;允许放入null元素&#xff0c;底层通过数组实现。除该类未实现同步外&#xff0c;其余跟Vector大致相同。每个ArrayList都有一个容量(capacity)&#xff0c;表示底层…

vscode写markdown(引入html及css语法)

vscode写markdown 下载插件插入代码markdown中引入html和css语法导出pdf 下载插件 Markdown All in OneMarkdown Preview EnhancedPaste Image 插入代码 afdfafamarkdown中引入html和css语法 呵呵 用ctrlshiftp 搜索"Markdown Preview Enhanced:Customize CSS"在…

个人旅游网(2)——功能详解——用户登录注册

文章目录 一、用户登录1.1、接口详解1.1.1、isLogged (判断用户是否登录)1.1.2、find-by-telephone&#xff08;输入手机号时校验手机号是否注册&#xff09;1.1.3、send-message&#xff08;发送验证码&#xff09;1.1.4、login-by-telephone&#xff08;登录按钮&#xff09;…