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

在这里插入图片描述

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

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

相关文章

华硕主板开机只进入Bios模式不进入Windows系统

华硕电脑进入bios界面解决办法 1.可以按平常方法多重启几下(bios界面按f10确认保存) 2.是固态硬盘的地方看看是否有松动 插回去就可以了3.图形界面bios先按f7进入高级模式,【security】菜单,通过方向键选择【secure Boot】选项&am…

【MATLAB库函数系列】线性调频Z(Chirp-Z,CZT)的MATLAB源码和C语言实现

在上一篇博客 【数字信号处理】线性调频Z(Chirp-Z,CZT)算法详解 已经详细介绍了CZT变换的应用背景和原理,先回顾一下: 回顾CZT算法 采用 FFT 算法可以很快计算出全部 N N N点 DFT 值,即Z变换 X ( z ) X(z) <

Python 数据分析 Matplotlib篇 增加注释【plt.text() plt.annotate()】(第3讲)

Python 数据分析 Matplotlib篇 增加注释【plt.text() & plt.annotate()】(第3讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

C# SqlSugar 数据库 T4模板

生成效果 模板代码 <# template debug"false" hostspecific"true" language"C#" #> <# output extension".cs" #> <# assembly name"System.Core" #> <# assembly name"System.Data" #>…

2024 年全球顶级的 4 款 PDF 转换器软件

PDF 是一种广泛使用的共享文档和文件的格式。但是&#xff0c;有时您可能需要将 PDF 文件转换为其他格式&#xff08;例如 Word 或 Excel&#xff09;&#xff0c;以便编辑或操作内容。这就是 PDF 转换器软件派上用场的地方。 有许多 PDF 转换器软件可供选择&#xff0c;有免费…

Java进阶(第六期): Arrays类(数组工具)、冒泡排序、选择排序、二分查找、【正则表达式】、Java正则爬取信息

文章目录 一、Arrays1.1代码示例&#xff1a; 二、冒泡排序2.1 代码示例 三、选择排序3.1 代码示例 四、二分查找4.1 代码示例 &#xff08;这里采用乱序数组&#xff09; 五、正则表达式5.1 正则表达式的基本使用5.2 正则表达式爬取信息练习 Java进阶&#xff08;第六期&#…

CUMT--Java复习--文件及IO流

目录 一、文件 1、文件系统和路径 2、File类 3、FilenameFilter接口 二、IO流 1、流的分类 2、流的体系结构 三、字节流 1、InputStream 2、OutputStream 四、字符流 1、Reader 2、Writer 五、过滤流和转换流 1、过滤流 2、转换流 六、序列化 1、对象序列化…

使用PE信息查看工具和Dependency Walker工具排查因为库版本不对导致程序启动报错的问题

目录 1、问题说明 2、问题分析思路 3、问题分析过程 3.1、使用Dependency Walker打开软件主程序&#xff0c;查看库与库的依赖关系&#xff0c;找出出问题的库 3.2、使用PE工具查看dll库的时间戳 3.3、解决办法 4、最后 VC常用功能开发汇总&#xff08;专栏文章列表&…

SRE 与 DevOps:你知道它们之间区别吗?

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享 DevOps专注于消除阻碍开发和运维之间协作的隔阂&#xff0c;而SRE致力于设计和实施可扩展、可靠的系统&#xff0c;确保最大可靠性。 这篇文章将探讨DevOps和SRE之间的差异&#xff0c…

微信商户申请0.2费率怎么开通

作为一个过来人和普通商家&#xff0c;在申请收款功能的时候找过不下十家机构&#xff0c;市面上比较知名的支付公司我都去找了&#xff0c;但是了解到的收款手续费率都在0.6左右&#xff0c;最低的也才0.38&#xff0c;收获了很多经验&#xff0c;不对&#xff0c;是教训&…

TCP服务器的演变过程:多进程实现一对多的TCP服务器

使用多进程实现一对多的TCP服务器 一、前言二、新增使用的fork()函数三、实现步骤四、完整代码五、TCP客户端5.1、自己实现一个TCP客户端5.2、Windows下可以使用NetAssist的网络助手工具 小结 一、前言 手把手教你从0开始编写TCP服务器程序&#xff0c;体验开局一块砖&#xf…

C语言、c++实现超好玩植物大战僵尸(完整版附源码)

实现这个游戏需要Easy_X main.cpp //开发日志 //1导入素材 //2实现最开始的游戏场景 //3实现游戏顶部的工具栏 //4实现工具栏里面的游戏卡牌 #define WIN_WIDTH 900 #define WIN_HEIGHT 600 //定义植物类型 enum { WAN_DOU, XIANG_RI_KUI, ZHI_WU_COUNT }; #include<stdio.…