逻辑地址 线性地址 物理地址 Linux kernel 内存管理设计

 linux kernel 2.6以后的MM,受到了兼容 risc arch cpu 的 MM 的启发,新的 MM 架构对 x86 上任务切换的效率上也有明显提高。

新的MM架构,GDT 不再随着进程的创建与结束而创建和删除 新的表项。

TSS段 也只有一个,进程切换时,只修改其中的一些bit的信息,TSS的 SS0, esp0,io bitmap 即可。

1,机器解析的思路

发现网络上大量的教程,多是以讹传讹地讲解 Linux 内存管理;

都是在讲:

逻辑地址  ->  线性地址  ->  物理地址

如果谙熟 Linux 制定的GPT和编译原理或对二进制分析比较熟练的话,会发现线性地址的值,因为段选择子指向的段描述符中的基地址(内核数据段、代码段和用户数据段、代码段)设置为0x00000000,因此在不同进程之间会比较接近 ,或者说是类似的一套,即Linux 进程中的 逻辑地址就是线性地址。

线性地址 = 段基地址 + 逻辑地址偏移 = 0x00000000 + 逻辑地址偏移 = 逻辑地址

但,基于此,不同的进程,却能解析出完全不同的物理地址,这个转换关系是怎么发生的。

上面这个过程确实是程序运行时地址的翻译顺序;

但是,程序之所以按照这个顺序来解析地址,是因为前期有人类中的一些大脑袋巧妙地按照相反到方向

物理地址  ->  线性地址  ->   逻辑地址

设计了这个内存管理系统;

2,人类设计mm的思路


在x86架构中,页表(Page Table)和页目录(Page Directory)是用于虚拟内存管理的重要数据结构。x86架构使用两级页表结构,包括页目录、页表和物理页框。

页目录(Page Directory)是一个包含1024个32位项的数组,每个项指向一个页表。页表(Page Table)是一个包含1024个32位项的数组,每个项指向一个物理页框。

x86架构中的虚拟地址被分为三部分:10位页目录索引、10位页表索引和12位页内偏移。通过这三部分可以定位到物理内存中的具体位置。

页表和页目录的规划方法通常包括以下步骤:

1. 页表初始化:

        在操作系统启动时,会初始化页表和页目录,建立虚拟地址到物理地址的映射关系。

2. 页表项填充:

        将页表和页目录中的项填充为合适的值,包括物理页框的地址和权限信息。

3. 页表更新:

        在进程切换或内存分配时,需要更新页表和页目录中的项,以反映新的虚拟地址到物理地址的映射关系。

4. TLB缓存:

        为了加速地址转换过程,x86架构使用TLB(Translation Lookaside Buffer)缓存页表项,减少内存访问次数。

 上图是前向推理地址,笼统地解释x86保护模式下,逻辑地址如何转换成物理地址;

下图是后向反推,这里为了理想化地解释页目录与页表和页框的关系,页框被认为是真实的物理内存,连续的4k一个构成一个页框;

实际上进程的页表与页框的关系,并不是这么工整金字塔式的层级关系,而是由操作系统帮助进程分配和填充的,按照Linux kernel 中的内存管理模块的规则。

事实上,从  逻辑地址 ->  线性地址,是很简单的,基本上就是段选择子帮助挑选出来的全局描述符表项(GDT)中的段基地址,加上偏移,就等于,等于,等于,就是线性地址。

这个很丝滑,很简单;

然后,线性地址 ->  物理地址 也很丝滑:

线性地址  =  页目录索引    |     页表索引     |      页框内偏移

这很容易查找和计算处具体的物理地址;

但是,当将这两种丝滑放在一起是,马上容易懵掉。为什么两个阶段都这么丝滑,以至于无法理解。线性地址这个桥梁很神奇,得到线性地址的过程,与解析线性地址的含义,是两个不相交的世界。  这是问题的核心。

在这两个世界中打穿虫洞的方法,是 Linux 对CR3 寄存器的使用。

这个秘密值两万元以上的课程费,都不一定能听清楚  ^ ^

3, CR3 寄存器与进程的页目录

3.1 页目录

当 Linux 系统启动一个新的程序时,操作系统会为该程序创建一个新的页目录,这是为了实现每个进程的地址空间隔离和独立性。这个过程是虚拟内存管理的一部分,确保每个进程拥有自己的虚拟地址空间,从而提高系统的安全性和稳定性。

3.1.1创建新的页目录的步骤:

1. 分配页目录:

  • 操作系统为新进程分配一个新的页目录。这个页目录是一个包含多个页目录项(PDEs)的表,每个页目录项指向一个页表。

2. 初始化页目录:

  • 新的页目录会被初始化,其中一些条目会被设置为指向操作系统的共享页表,如那些包含共享库(如 C 标准库等)的页表。
  • 其他页目录项会被设置为指向新分配的页表,这些页表用于存储进程特有的数据,如代码段、数据段和堆栈。

3. 映射虚拟地址到物理地址:

  • 操作系统将程序的代码和数据加载到内存中,并在页表中创建相应的映射,将虚拟地址映射到物理内存地址。
  • 这包括设置适当的访问权限,例如,代码段可能被设置为只读和可执行,而数据段可能被设置为可读写。

4. 切换到新的页目录:

  • 在进程开始执行前,CPU 的页目录基址寄存器(CR3)会被更新为指向新进程的页目录的物理地址。
  • 这确保了当新进程开始执行时,CPU 的内存访问将根据新进程的页目录进行解析。

3.2 每个进程创建新的页目录的目的

  • 隔离性:每个进程拥有独立的页目录和页表,确保了进程间的内存隔离,一个进程不能直接访问或修改另一个进程的内存空间。
  • 安全性:通过为每个进程设置不同的内存访问权限,操作系统可以防止不当的内存访问,如阻止进程执行非代码区域的内存。
  • 稳定性:内存隔离减少了进程间相互影响的可能性,从而提高了系统的整体稳定性。

这种基于页目录和页表的内存管理机制是现代操作系统支持复杂多任务环境的基础,允许系统同时运行多个程序,而每个程序都在其自己保护的内存空间内运行。

3.2 CR3 与页目录

在 x86 架构中,CR3 寄存器(也称为页目录基址寄存器 PDBR)在将线性地址转换为物理地址的过程中起着至关重要的作用。
CR3 寄存器存储着当前使用的页目录的物理地址,这是虚拟内存管理中的一个关键组件。
CR3 寄存器的作用:
1. 存储页目录的物理地址
CR3 寄存器包含当前活动的页目录的物理内存地址。这个页目录包含了多个页目录项(PDEs),每个页目录项指向一个页表。
2. 启动地址转换
当处理器需要将线性地址(虚拟地址)转换为物理地址时,它首先访问 CR3 寄存器以获取页目录的位置。
处理器使用线性地址的高位部分(通常是最高的几位)来索引页目录,找到对应的页目录项,该项指向具体的页表。
接着,处理器使用线性地址的中间部分来索引该页表,找到对应的页表项(PTE),该项包含最终的物理页面地址。
最后,线性地址的低位部分(页内偏移)被添加到页面地址中,形成完整的物理地址。
3. 上下文切换时的角色
在进行进程切换时,操作系统会更新 CR3 寄存器以指向新进程的页目录的物理地址。这确保了每个进程都使用其自己的地址空间,从而实现内存隔离和保护。
更新 CR3 寄存器会导致处理器的页表缓存(TLB, Translation Lookaside Buffer)被刷新,以确保地址转换不会使用旧的、属于前一个进程的映射。

CR3 寄存器是实现 x86 架构中基于分页的虚拟内存管理的核心。
它不仅指示当前页目录的物理位置,还在进程上下文切换中发挥着重要作用,确保每个进程的内存空间得到正确管理和保护。

4, 结论分析:双重模板

由于页目录这个与进程一 一绑定的结构体,使得不同进程的线性地址的计算,都可以是简单地表示为 段基址 +  偏移,使得不同进程中的逻辑地址得到非常相似的 线性地址,但是在解析线性地址时,由于CR3所指向的页目录的不同,使得第三次丝滑地从相似的线性地址,推导出迥异的物理地址。

因此,不同进程之内线性地址的简单一致性,与得到具体物理地址的迥异复杂性,跟页目录的巧妙使用密不可分。

CR3

Linus 的这个设计中,叠加了双重模板编程思想:

<1.> 不同进程,只需要更换 CR3 这一个参数的值即可实现地址保护;

<2.> 不同 arch 的 CPU 只需要拿出一个寄存器来扮演 x86 中的 CR3, 即复用 Linux kernel中 相同的 MM 代码逻辑。

5,复盘整个过程

盯着这个图,思考上述过程,感受线性地址到物理地址过程中,页目录与CR3,这个三个丝滑的过程中所作用:

6,Linux 弱化了x86 架构中 gptr 的目的

Linus 处于对 Linux 通用性的考虑,鉴于 risc CPU 的 MM 都比较简单,risc 的体系结构中基本没有分段的思想体现,所以Linux kernel的MM思维中故意弱化了 x86 的 GPTR 寄存器的作用。主要发挥页目录 和 MMU 的功能,在物理内存分页的思想上,来实现进程空间的保护功能。

叹为观止

值100亿美元

致敬 《有机会赚100亿却只想写代码,Linux之父的传奇前半生》

综上所述,这个MM难理解也是正常的,确实是多个方面都巧夺天工

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

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

相关文章

深度学习入门2—— 神经网络的组成和3层神经网络的实现

由上一章结尾&#xff0c;我们知道神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数。接下来会介绍神经网络的概要&#xff0c;然后再结合手写数字识别案例进行介绍。 1.神经网络概要 1.1从感知机到神经网 我们可以用图来表示神经网络&#xff0c;我们把最…

上位机图像处理和嵌入式模块部署(mcu之静态库生成和使用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多同学做了很长时间的mcu和keil开发&#xff0c;都认为keil工程中必须包含所有的源代码&#xff0c;其实这是不对的。如果有一些代码我们不希望别…

IKVM.net调用Jar包实现SM4解密

近期&#xff0c;我深入学习了如何使用IKVM.net来调用Jar包&#xff0c;这次的学习经历让我对Java与.NET之间的互操作性有了更深刻的理解。IKVM.net作为一款强大的工具&#xff0c;为我们打通了Java与.NET之间的桥梁&#xff0c;使得在.NET环境中调用Java库变得简单而高效。 在…

[数据集][目标检测]棉花叶子害虫检测数据集VOC+YOLO格式571张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;595 标注数量(xml文件个数)&#xff1a;595 标注数量(txt文件个数)&#xff1a;595 标注类别…

Linux驱动调试——使用DEVICE_ATTR实现cat、echo指令调试驱动

在平常做一些驱动调试的时候&#xff0c;每次都写应用去调试相对较麻烦&#xff0c;有一个非常便捷的操作方法就是使用device_attr&#xff0c;只需要执行shell指令例如echo和cat就可以看到效果&#xff0c;不需要再单独写一个测试demo。 看网上很多博客在这一块的使用上写的都…

FENDI CLUB精酿啤酒与小麦的不解之缘

FENDI CLUB精酿啤酒与小麦之间这种联系体现在啤酒的酿造原料、口感特色以及文化内涵等多个方面。以下是关于这两者之间关系的详细分析&#xff1a; 一、酿造原料的紧密联系 小麦作为关键原料&#xff1a;FENDI CLUB精酿啤酒在酿造过程中&#xff0c;小麦是不可或缺的原料之一…

Mybatis 系列全解(1)——全网免费最细最全,手把手教,学完就可做项目!

Mybatis 系列全解&#xff08;1&#xff09; 1. 第一个小程序2. CURD 增删改查3. 模糊查询4. 配置解析4.1 核心配置文件4.2 环境配置4.3 属性4.4 类型别名4.5 设置4.6 映射器 mappers 1. 第一个小程序 1&#xff09;创建一个数据库&#xff0c;一个表&#xff0c;填入一些数据…

Python3简单实现与Java的Hutool库SM2的加解密互通

1、背景&#xff1a; 因业务需求&#xff0c;需要与某平台接口对接。平台是Java基于Hutool库实现的SM2加密解密&#xff0c;研究了下SM2的加解密算法&#xff0c;网上找的资料&#xff0c;都是说SM2【椭圆曲线】 公钥长【x,y分量 64字节】&#xff0c;私钥短【32字节】&#x…

华为---OSPF被动接口配置(四)

9.4 OSPF被动接口配置 9.4.1 原理概述 OSPF被动接口也称抑制接口&#xff0c;成为被动接口后&#xff0c;将不会接收和发送OSPF报文。如果要使OSPF路由信息不被某一网络中的路由器获得且使本地路由器不接收网络中其他路由器发布的路由更新信息&#xff0c;即已运行在OSPF协议…

FuTalk设计周刊-Vol.031

&#x1f525;AI漫谈 热点捕手 1、如何用自然语言 5 分钟构建个人知识库应用&#xff1f;我的 GPTs builder 尝试 开发者的想象力闸门一旦打开&#xff0c;迎接我们的必然是目不暇接的 AI 应用浪潮冲击。 链接https://sspai.com/post/84325 2、GPT-4 Turbo、功能融合&#x…

【机器学习】大模型驱动下的医疗诊断应用

摘要&#xff1a; 随着科技的不断发展&#xff0c;机器学习在医疗领域的应用日益广泛。特别是在大模型的驱动下&#xff0c;机器学习为医疗诊断带来了革命性的变化。本文详细探讨了机器学习在医疗诊断中的应用&#xff0c;包括疾病预测、图像识别、基因分析等方面&#xff0c;并…

LCR 142.训练计划IV

1.题目要求: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ int compare(const void* a,const void* b) {return (*(int*)a - *(int*)b); } struct ListNode* trainningPlan(struct ListNode* l1, struct Li…

【数据结构】第十九弹---C语言实现冒泡排序算法

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、冒泡排序基本思想 2、代码的初步实现 3、代码的优化 4、代码的测试 5、时空复杂度分析 6、模拟实现qsort 6.1、冒泡排序函数 6.2、交换数…

针对 AI 优化数据湖仓一体:使用 MinIO 仔细了解 RisingWave

RisingWave 是现代数据湖仓一体处理层中的开源流数据库&#xff0c;专为性能和可扩展性而构建。RisingWave 旨在允许开发人员在流数据上运行 SQL。鉴于 SQL 是数据工程的通用语言&#xff0c;此功能非常重要。它具有强大的架构&#xff0c;包括计算节点、元节点和压缩器节点&am…

分享一个 Fail2ban 过滤规则

今天明月给大家分享个 Fail2ban 的过滤&#xff08;Filter&#xff09;规则&#xff0c;有关 Fail2ban 的文章大家可以参考【服务器全面使用 Fail2Ban 初见成效】和【使用 Fail2ban 禁止垃圾采集爬虫&#xff0c;保护 Nginx 服务器】等文了解&#xff0c;总之 Fail2ban 是 Linu…

分流井设备的监测控制和智慧运维

分流井是一种用于将雨水和污水进行分离的设施&#xff0c;通常设置在雨水管和污水管的汇合处。它可以根据不同的情况&#xff0c;将雨水和污水分别排放到不同的管道中&#xff0c;从而实现雨污分流的目的。 以下是一些常见的分流井类型和工作原理&#xff1a; 1、智能分流井&a…

java-SpringBoot执行定时任务-任务调度-@EnableScheduling和@Scheduled

文章目录 java借助springBoot框架&#xff0c;执行定时任务0. 项目地址1. 需求分析2、新建springBoot项目3. 编写定时任务3.1 开启调度任务3.2 编写定时任务方法 java借助springBoot框架&#xff0c;执行定时任务 0. 项目地址 https://github.com/OrangeHza/JavaDemo 1. 需求…

Redis小对象压缩

小对象压缩存储 如果Redis内部管理的集合数据结构很小&#xff0c;他会使用紧凑存储形式压缩存储。 Redis的ziplist是一个紧凑的字节数组结构&#xff0c;如下图所示&#xff0c;每个元素之间都是紧挨着的。 如果他存储的是hash结构&#xff0c;那么key和value会作为两个ent…

竞赛 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

Maven添加reactor依赖失败

目录 情况说明 解决过程 情况说明 起初是自己在学spring boot3&#xff0c;结果到了reactor这一部分的时候&#xff0c;在项目的pom.xml文件中添加下列依赖报错&#xff1a; <dependencyManagement><dependencies><dependency><groupId>io.projectr…