linux内存寻址原来那么简单

内存寻址

内存寻址听起来高大上,其实真实处理起来很简单,以常见的80x86架构为例,有三种不同的地址:

  • 逻辑地址
  • 线性地址
  • 物理地址

地址转换关系
内存控制单元(MMU)通过分段单元的硬件电路把一个逻辑地址转化为线性地址,通过分页单元的硬件电路把线性地址转换为一个物理地址,如上图。

逻辑地址(logical address) : 用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段和偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离。

线性地址(linear address)也常被称为虚拟地址 : 是一个32位的无符号整数,取值范围0x00000000 - 0xffffffff

物理地址(physical address) : 内存芯片级内存单元寻址。

饭要一口一口吃,路要一步一步走,通过以上介绍我们明白了三种地址之间的关系,接下来我们分步拆解地址转换的过程:

分段单元(Segmentation Uint)

通过地址转换关系一图,我们可以知道逻辑地址需要通过分段单元才能转换为线性地址(linear address)。下图就是分段单元的实现:

分段单元

逻辑地址并不是真正的地址,它由段和偏移量组成。 想要得到段,我们首先要知道段是什么段又在哪?

段是代码段、数据段、栈段,按照是否是内核态,又可以分为内核代码段、内核数据段、内核栈段,用户代码段、用户数据段、用户栈段,图中给出的Selector就是用来确定使用哪个段的。

Selector又称为段选择符,或者段选择器,我更倾向于称其为段选择器,段选择器的实现如下图:
在这里插入图片描述
段选择器由三部分组成:

  • index - 索引号
  • TI - 表指示器
  • RPL - 请求者特权级别

从图中我们能够看出,Index乘以8能定位出Descriptor中的段描述符,TI(Table indicator)指示出是gdt还是在ldt中,TI=0说明段描述符在gdt中,TI=1说明段描述符在ldt中。

那问题来了,段选择器又是从哪里来的,我们编程通常接触不到这些,就算是看汇编也只接触到一些寄存器就结束了,因此为了程序能够控制段选择器,处理器提供了段寄存器,顾名思义,段寄存器就是用来存放段选择符(器)的地方,常见的有cs,ss,ds,es,fs和gs,其中有三个有专门的用途:

  • cs - 代码段寄存器
  • ss - 栈段寄存器
  • ds - 数据段寄存器

**注意:**cs寄存器中包含一个两位的字段,用以指名CPU当前的特权级别(Current privilege level, CPL),该字段只有两个值0和3,0代表最高优先级,3代表最低优先级。

段描述符的大小为8字节,因此、通过index * 8能定位出段描述符的偏移量,TI的值指示出应该去gdtr还是去ldtr寄存器中取值。

.段描述符
image::image-2023-12-23-21-59-35-245.png[]

如何计算出段描述符地址?

通过分段单元的图我们可以看到,index * 8 + (TI指定的描述表)就能得到段描述符的地址。
因此,如果TI=0,GDT在0x00020000(这个值保存在gdtr寄存器中),index的值为2,那么短描述符地址就是 2 * 8 + 0x00020000 = 0x00020010

如何计算出线性地址?

从段描述符一图中,我们看到很多部分,我们在计算线性地址之前需要先了解一下该部分的组成:

字段名描述
Base指向段首字节的线性地址
G粒度标识,0代表以字节为单位,否则以4096字节为的倍数单位
Limit存放段中最后一个内存单元的偏移量,从而决定段的长度
S0 代表系统段,存储诸如LDT这种关键数据结构,否则它是一个普通的代码段或数据段
Type段的特征
DPL描述符特权级别
P是否在主存中
D或B取决于是代码段还是数据段

.段描述符各个字段的含义
image::image-2023-12-23-22-37-31-014.png[]

搞明白以上关系之后我们就可以轻松的计算出线性地址了,线性地址就是逻辑地址的偏移量(offset)和段描述符Base字段相加的值。


以上就是通过分段单元实现的逻辑地址->线性地址的转换


分页单元(paging unit)

相对于分段单元,分页单元复杂许多,但是说起来又简单许多,为什么这样说呢?因为分段主要靠硬件,而分页主要靠软件。

分页单元就是把线性地址转换为物理地址,其中最主要的一个任务就是把锁请求的访问类型与线性地址的访问权限相比较,如果这次的访问权限是无效的就产生一个缺页异常。

线性地址被分为固定长度为单位的组,称为页。页内部连续的线性地址会被映射到物理地址上。

以一种常见的页划分为例,32位的线性地址会被分为3个域:

  • Directory(目录) - 10位
  • Table(页表) - 10位
  • Offset(偏移量) - 12位

真在使用的页目录的物理地址存放在控制寄存器cr3中,其常见组成形式可以用如下如表示:

.paging 80x86 processors
image::image-2023-12-24-15-59-48-992.png[]

这种形式的目录结构寻址能力可以高达 1024 * 1024 * 4096 = 232

在linux上为了应对64位系统对内存的需求,使用了更多级的目录来进行内存的分页,其分页形式为:

  • 页全局目录(Page Global Directory)
  • 页上级目录(Page Upper Directory)
  • 页中间目录(Page Middle Directory)
  • 页表(Page Table)

其组成形式如图所示:

.Linux paging model
image::image-2023-12-24-16-05-50-650.png[]


以上就是计算机进行内存寻址的全过程,当然分页单元的过程主要是软件实现的,这里没有对linux的实现接口进行说明,如果感兴趣的可以下载linux 2.6版本查看,虽然新版本的linux分页单元有改动,但是还是推荐你看下2.6版本的,这个版本的功能实现更加的纯粹,也更容易理解。


文章以使用asciidoc形式上传到github上,需要的可以自行下载

https://github.com/zzu-andrew/note_book

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单例模式(C++实现)

RAII运用 只能在栈上创建对象 只能在堆上创建的对象 单例模式 设计模式 懒汉模式 解决线程安全 优化 饿汉模式 饿汉和懒汉的区别

Vue3学习(后端开发)

目录 一、安装Node.js 二、创建Vue3工程 三、用VSCode打开 四、源代码目录src 五、入门案例——手写src 六、测试案例 七、ref和reactive的区别 一、安装Node.js 下载20.10.0 LTS版本 https://nodejs.org/en 使用node命令检验安装是否成功 node 二、创建Vue3工程 在…

C语言--if...else语句【语法讲解】

一.if...else语句的介绍 if…else 语句是编程中常用的一种分支语句,用于根据条件执行不同的操作。 它的基本语法如下: if (条件表达式) {// 当条件表达式为真时执行的代码块 } else {// 当条件表达式为假时执行的代码块 } 当条件表达式为真时&#xff…

互联网上门洗衣洗鞋小程序优势有哪些?

互联网洗鞋店小程序相较于传统洗鞋方式,具有以下优势; 1. 便捷性:用户只需通过手机即可随时随地下单并查询,省去了许多不必要的时间和精力。学生们无需走出宿舍或校园,就能轻松预约洗鞋并取件。 2. 精准定位&#xff1…

前菜---二叉树+堆的小练习

目录 前言🏜️ 1. 二叉树性质总结⛱️ 1.2 性质3⏰ 2. 二叉树性质小练习🏕️ 3. 答案解析💡 4. 堆概念结构小练习🪔 5. 答案解析🧿 6. 前/中/后/层序遍历小练习🔫 7. 答案解析🧺 后语…

祝大家圣诞节快乐

同时庆祝 JWFD 20周年

c++代码寻找USB00端口并添加打印机

USB00*端口的背景 插入USB端口的打印机,安装打印机驱动,在控制面板设备与打印机处的打印机对象上右击,可以看到打印机端口。对于不少型号,这个端口是USB001或USB002之类的。 经观察,这些USB00*端口并不是打印机驱动所…

Seata 序列化问题

异常: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id handling not implemented for type java.lang.Object (by serializer of type com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer) (through reference chain: i…

新建项目EasyUiAutotest,安装Appium-Python-Client

一、前置说明 Appium-Python-Client 是 Appium 的 Python 客户端库,它提供了一系列的类和方法,用于与 Appium 服务器进行通信,并执行各种移动应用测试操作,包括启动应用、模拟用户输入、点击等操作。 二、操作步骤 1. 启动Pych…

Exynos4412 移植Linux-6.1(九)移植tiny4412_backlight驱动的过程及问题解决

系列文章目录 Exynos4412 移植Linux-6.1(一)下载、配置、编译Linux-6.1 Exynos4412 移植Linux-6.1(二)SD卡驱动——解决无法挂载SD卡的根文件系统 Exynos4412 移植Linux-6.1(三)SD卡驱动——解决mmc0: Ti…

写了这么多年DateUtils,殊不知你还有这么多弯弯绕!

目录 在日常开发中,Date工具类使用频率相对较高,大家通常都会这样写:这很简单啊,有什么争议吗?格式化后出现的时间错乱。看看Java 8是如何解决时区问题的:在处理带时区的国际化时间问题,推荐使用…

LSTM和GRU vs 循环神经网络RNN

1、考虑下列三种情况下,对比一下普通RNN的表现和LSTM和GRU表现: (1)早期观测值对预测未来观测者具有非常重要的意义。 考虑一个极端情况,其中第一个观测值包含一个校验和, 目标是在序列的末尾辨别校验和是…

第十四章 集合(List)

一、集合框架体系 集合: (1)可以动态保存任意多个对象。 (2)提供了一系列方便的操作对象的方法:add、remove、set、get等。 二、Collection 1. Collection 接口常用方法 (1)add&a…

BP网络识别26个英文字母matlab

wx供重浩:创享日记 对话框发送:字母识别 获取完整源码源工程文件 一、 设计思想 字符识别在现代日常生活的应用越来越广泛,比如车辆牌照自动识别系统,手写识别系统,办公自动化等等。本文采用BP网络对26个英文字母进行…

第 377 场周赛 解题报告 | 珂学家 | Floyd + 划分型DP

前言 整体评价 天崩局,压哨绝杀,感谢天,感谢地,T_T. 感觉被T2玩惨了,T3和T4很像,无非一个贪心,一个是划分型DP,但是都需要基于floyd预处理。 T1. 最小数字游戏 思路: …

接口测试 — 11.logging日志模块处理流程

1、概括理解 了解了四大组件的基本定义之后,我们通过图示的方式来理解下信息的传递过程: 也就是获取的日志信息,进入到Logger日志器中,传递给处理器确定要输出到哪里,然后进行过滤器筛选,通过后再按照定义…

【LeetCode】链表精选11题

目录 快慢指针: 1. 相交链表(简单) 2. 环形链表(简单) 3. 快乐数(简单) 4. 环形链表 II(中等) 5. 删除链表的倒数第 N 个节点(中等) 递归迭…

量化投资策略的评估标准及其计算公式

收益率指标:分为策略的总收益率和策略的年化收益率 策略的总收益率: 策略的总收益率是评价一个策略盈利能力的最基本的指标,其计算方法为: 公式中Vt表示策略最终的股票和现金的总价值,V0表示策略最初的股票和现金的总…

探秘JDK 13的黑科技:新特性一览

欢迎来到我的博客,代码的世界里,每一行都是一个故事 探秘JDK 13的黑科技:新特性一览 前言switch表达式扩展Switch表达式的基本概念:使用Switch表达式的优势:示例代码:注意事项和最佳实践: Text …

Spring Cloud + Vue前后端分离-第7章 核心业务功能开发

Spring Cloud Vue前后端分离-第7章 核心业务功能开发 7-1 课程管理功能开发 课程管理页面美化 1.课程管理页面美化 demo-course.jpg 复制search.html中的部分代码 course.vue 看效果 测试一下新增修改删除效果 1.课程管理页面美化2 scoped:style下的样式只应用于当前组件…