逻辑地址、线性地址、物理地址的关系以及段寄存器在不同位数CPU中的用途演变以及GDT LDT PGD PT的关系

本文深入探讨了计算机内存管理的核心概念,包括虚拟地址、线性地址、逻辑地址和物理地址的定义与转换过程,揭示了现代操作系统如何高效管理内存,实现多进程间的内存隔离。

历史

         许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以K为单位进行计算,相应的,当时的程序规模也不大,所以内存容量虽然小,但还是可以容纳当时的程序。但随着图形界面的兴起还有用户需求的不断增大,应用程序的规模也随之膨胀起来,终于一个难题出现在程序员的面前,那就是应用程序太大以至于内存容纳不下该程序,通常解决的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块0首先运行,结束时他将调用另一个覆盖块。虽然覆盖块的交换是由OS完成的,但是必须先由程序员把程序先进行分割,这是一个费时费力的工作,而且相当枯燥。人们必须找到更好的办法从根本上解决这个问题。不久人们找到了一个办法,这就是虚拟存储器(virtual memory).虚拟存储器的基本思想是程序,数据,堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。比如对一个16MB的程序和一个内存只有4MB的机器,操作系统通过选择,可以决定各个时刻将哪4M的内容保留在内存中,并在需要时在内存和磁盘间交换程序片段,这样就可以把这个16M的程序运行在一个只具有4M内存机器上了。而这个16M的程序在运行前不必由程序员进行分割。

物理地址、虚拟地址(线性地址)、逻辑地址

     任何时候,计算机上都存在一个程序能够产生的地址集合,我们称之为地址范围。这个范围的大小由CPU的位数决定,例如一个32位的CPU,它的地址范围是0~0xFFFFFFFF (4G,2的32次方),而对于一个64位的CPU,它的地址范围为0~0xFFFFFFFFFFFFFFFF (64T,2的64次方).这个范围就是我们的程序能够产生的地址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集。这里举一个最简单的例子直观地说明这两者,对于一台内存为256M的32bit x86主机来说,它的虚拟地址空间范围是0~0xFFFFFFFF(4G),而物理地址空间范围是0x000000000~0x0FFFFFFF(256M)。

      这里有一个虚拟内存的概念,虚拟内存(virtual memory)是对整个内存(不要和机器上插那条对上号)的抽像描述。它是相对于物理内存来讲的,能直接理解成“不真实的”,“假的”内存,例如,一个0x08000000内存地址,他并不对就物理地址上那个大数组中0x08000000 - 1那个地址元素;之所以是这样,是因为现代操作系统都提供了一种内存管理的抽像,即虚拟内存(virtual memory)。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址这个“转换”,是所有问题讨论的关键。
      有了这样的抽像,一个程序,就能使用比真实物理地址大得多的地址空间(拆东墙,补西墙,银行也是这样子做的),甚至多个进程能使用相同的地址。不奇怪,因为转换后的物理地址并非相同的。

  物理地址,CPU地址总线传来的地址,由硬件电路控制(现在这些硬件是可编程的了)其具体含义。物理地址中很大一部分是留给内存条中的内存的,但也常被映射到其他存储器上(如显存、BIOS等)。在没有使用虚拟存储器的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储器被读写;而在使用了虚拟存储器的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到存储器管理单元MMU,把虚拟地址映射为物理地址,然后才能送到内存地址总线上,去物理内存访问数据。

        线性地址(Linear Address)也叫虚拟地址(virtual address)是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。是一个32位无符号整数,可以用来表示高达4GB的地址,也就是,高达4294967296个内存单元。线性地址通常用十六进制数字表示,值得范围从0x00000000到0xfffffff)程序代码会产生逻辑地址,通过逻辑地址变换就可以生成一个线性地址。如果启用了分页机制,那么线性地址可以再经过变换以产生一个物理地址。如果没有启用分页机制,那么线性地址直接就是物理地址。

        逻辑地址是在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址,也就是是机器语言指令中,用来指定一个操作数或是一条指令的地址。要经过寻址方式的计算或变换才得到内存储器中的实际有效地址即物理地址。一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是个索引号,后面3位包含一些硬件细节 。

       CPU将一个逻辑地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程。之所以这样冗余,Intel完全是为了兼容而已(Intel为了兼容,将远古时代的段式内存管理方式保留了下来,x86体系的处理器刚开始时只有20根地址线,寻址寄存器是16位。我们知道16位的寄存器可以访问64K的地址空间,如果程序要想访问大于64K的内存,就需要把内存分段,每段64K,用段地址+偏移量的方式来访问,这样使20根地址线全用上,最大的寻址空间就可以到1M字节,这在当时已经是非常大的内存空间了。)。

       现代的多用户多进程操作系统,需要MMU, 才能达到每个用户进程都拥有自己独立的地址空间的目标。使用MMU, 操作系统划分出一段地址区域, 在这块地址区域中, 每个进程看到的内容都不一定一样。例如MICROSOFT WINDOWS操作系统将地址范围4M-2G划分为用户地址空间,进程A在地址0X400000(4M)映射了可执行文件,进程B同样在地址0X400000(4M)映射了可执行文件,如果A进程读地址0X400000, 读到的是A的可执行文件映射到RAM的内容,而进程B读取地址0X400000时,则读到的是B的可执行文件映射到RAM的内容。这就是MMU在当中进行地址转换所起的作用。

寄存器

首先看一下intel的早期cpu谱系(目前的基本都是64位了)

x86指Intel制造的普通CPU(提出x86这个表示法时,个人电脑上以32位Intel的CPU为主),x64是x86_64的缩写,指x86基础上的改进版(加入64位地址扩展等性能),而纯64位计算机架构用IA64表示,32位兼容的64位架构用amd64表示(AMD是这一架构的主要生产商)。由于Intel起步较早,影响较大,有时也把amd64架构的CPU称为x86_64架构。

8086(非常早期的CPU,是16位的CPU)中有4个16位的段寄存器:CS、DS、SS、ES,分别用于存放可执行代码的代码段、数据段、堆栈段和其他段的基地址。一个程序往往分为好几个段。CS中保存了代码段的基地址,DS保存的是数据段的基地址,而IP中保存的是所要执行的下一条指令的地址。cs是值cpu执行的当前指令的段地址,ds是数据开始的段地址。通俗来讲,知CS是告诉CPU,去哪道个位置找内容当成指令去执行,DS是告诉CPU,去哪个位置找内容当成数据被使用。CPU要执行CS中的指令,指令用到的数据可能就存放在DS中。你可内以把数据放到CS中 但是CPU并不把它当成数据来使用,你也可容以把指令放到DS中,但是CPU根本不去DS里读指令。它是怎么用的呢?这些段寄存器存放的是16位基地址,而CPU访存指令给出的也是16位的地址。但是这两类地址完全不同,它们代表的长度实际并不一样。基地址的16位实际代表了20位,也就是说它后面会加000,这才是真正的基地址,访存指令提供的地址叫内部地址。内部地址加上真正的基地址,得到的才是实际地址。也就是说在8086中,实际采用了20位的地址,那么对应的地址空间有多大呢?2^{20}共1M,这在8086年代已经很大啦,已经可以满足用户需求啦。

在 80386(也就是大名鼎鼎的奔3)中,增加了2个段寄存器,共有6个16位的段寄存器,CS、DS、SS、ES、FS、GS(后面3个是附加数据段寄存器)。但是,这些段寄存器中存放的不再是某个段的基地址,而是某个段的选择符(Selector)。之所以要进行转变,是因为(1)16位的寄存器无法存放32位的段基地址;(2)内存变大,远远超出了1M的大小。但是寻址还是要找到段基地址的,段寄存器既然放不了了,那怎么办呢?

段基地址只好存放在一个叫做描述符表(Descriptor)的表中。因此,在80386中,我们把段寄存器叫做选择符(子),这些段寄存器里面放的不再是基址,而是下标及其他控制信息(为什么不把段寄存器里面的内容全当成下标呢?那会有2^{16}=65536下标,实际上,描述符表并没有这么长,同时描述符表分成了全局描述符表GDT和局部描述符表LDT,且为了安全控制,要将“用户”进行分级控制,不能随便访问数据,所以还要进行用户控制,因此,这里只用了13位作为下标表示,共有8192个,余下3位中的1位表示查哪个描述符表,最后2位表示权限控制,00为系统内核态,11为用户态)。下面给出6个段寄存器的名称和用途:

 

地址转换

一、逻辑地址转线性地址

机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址才能够被访问到。

<think>首先,用户的问题是:"我想了解逻辑地址物理地址之间的关系 请问逻辑地址物理地址关系"。用户明确要求了解逻辑地址物理地址之间的关系。 我需要参考提供的引用资料: - 引用[1]:解释了逻辑地址(Logic Address)是程序产生的与段相关的偏移地址部分,例如指针变量的值。逻辑地址在实模式下等同于物理地址,但通常情况下是相对于进程数据段的地址。它是分段机制的一部分。 - 引用[2]:讨论了逻辑地址物理地址的转换过程,涉及段号和页内地址来计算物理地址。示例中,逻辑地址包括段号和页内地址,通过段首地址和偏移计算物理地址。 - 引用[3]:详细说明了逻辑地址物理地址的转换,通过线性地址作为中间层。逻辑地址加上段基地址生成线性地址,再转换为物理地址。 - 引用[4]:区分了各种地址类型,指出线性地址逻辑地址物理地址的中间层。逻辑地址加上段基地址得到线性地址;如果分页启用,线性地址转换为物理地址;否则,线性地址物理地址。 关键点总结: - **逻辑地址**:由程序产生的地址,通常是段内偏移地址。在C程序中,指针值就是逻辑地址。它是相对于段基地址的偏移。 - **线性地址**:逻辑地址加上段基地址后生成的地址。这在分段机制中发生。 - **物理地址**:实际内存中的硬件地址。如果分页机制启用,线性地址经过分页转换为物理地址;如果没有分页,线性地址直接是物理地址。 用户的问题聚焦在逻辑地址物理地址关系上,所以我需要直接解释这个关系,参考引用。 关系核心: 1. **转换过程**:逻辑地址转换为物理地址的过程涉及分段和可能的分页。 - 首先,逻辑地址(段标识符 + 段内偏移)通过分段机制转换为线性地址。 - 然后,如果启用分页,线性地址通过分页机制转换为物理地址。 - 如果只有分段没有分页,线性地址就是物理地址。 2. **在系统中的角色**: - 逻辑地址是应用程序使用的地址,它对程序员透明(如在C语言中使用指针)。 - 物理地址是硬件内存的实际地址。 3. **示例参考**:引用[2]给出了一个转换的例子: - 逻辑地址:段号2,页内地址154 - 段首地址480K(假设480*1024B) - 块号地址(可能是线性地址或其他)= 段首地址 + 偏移(这里似乎是直接计算物理地址) - 物理地址 = 480*1024 + 154 = 491674B 但这可能忽略了分页。需要澄清。 结合所有引用: - 逻辑地址 -> (通过分段) -> 线性地址 -> (通过分页) -> 物理地址 回答结构: - 先定义逻辑地址物理地址。 - 解释转换过程。 - 使用引用支持观点。 - 保持中文,结构清晰。 系统指令: - 行内数学表达式用$...$,如地址计算。 - 独立公式用$$...$$。 - 末尾添加引用标识,如[^1]。 - 生成相关问题。 用户上一次问题和回答:用户的问题是关于地址关系的,没有历史上下文。所以专注于当前问题。 最终回答结构: 1. 介绍逻辑地址物理地址。 2. 解释关系:转换过程。 3. 可能添加一个简单例子。 4. 生成相关问题。 相关问题示例: - 逻辑地址如何转换为物理地址? - 分段和分页机制在地址转换中的作用是什么? - 在实模式和保护模式下地址转换有何不同? 引用标识:在相关地方添加[^1], [^2]等。 </think>### 逻辑地址物理地址关系 在计算机系统中,逻辑地址物理地址是内存管理中核心的概念,它们之间的关系主要体现在地址转换过程中。逻辑地址是应用程序层使用的地址,而物理地址是硬件内存的真实地址。这种关系依赖于系统的内存管理机制,特别是分段和分页机制。以下我将基于您提供的引用资料详细解释,确保回答准确可靠。 #### 1. **定义逻辑地址物理地址** - **逻辑地址**:这是由程序(如C语言中的指针)产生的地址,它表示相对于段基地址的偏移量。例如,在C程序中,`&`操作符获取的指针值就是逻辑地址逻辑地址是相对于应用程序的段(如数据段)的地址,并不直接对应物理内存的位置。在Intel实模式下,逻辑地址等同于物理地址;但通常在现代保护模式下,它们需要通过转换才能得到物理地址[^1][^3]。 - **物理地址**:这是实际硬件内存中的地址,CPU通过它直接访问RAM或其他存储设备。物理地址是内存控制器使用的最终地址,对应用程序员完全透明[^3][^4]。 #### 2. **地址转换过程** 逻辑地址物理地址的转换通常涉及一个中间层——线性地址(也称为虚拟地址)。这个过程依赖于系统的内存管理单元(MMU),具体步骤如下: - **步骤1: 逻辑地址线性地址的转换** 逻辑地址由两部分组成:段标识符(segment selector)和段内偏移地址(offset)。通过分段机制,段标识符对应一个段描述符(在局部描述符表LDT或全局描述符表GDT中找到),该描述符包含段基地址(base address)。逻辑地址转换为线性地址的公式为: $$ \text{线性地址} = \text{段基地址} + \text{段内偏移地址} $$ 例如,假设逻辑地址的段号对应段基地址480 KB(即$480 \times 1024$字节),段内偏移为154字节,则线性地址为 $480 \times 1024 + 154$字节[^2][^3][^4]。这个线性地址是处理器可寻址的虚拟地址空间的一部分。 - **步骤2: 线性地址物理地址的转换** 如果不启用分页机制,线性地址直接等同于物理地址。但在大多数现代操作系统中,分页机制是启用的: - 分页机制将线性地址划分为页号(page number)和页内偏移(page offset)。 - 页号通过页表(page table)查找对应的物理页帧号(frame number)。 - 物理地址的计算公式为: $$ \text{物理地址} = \text{物理页帧号} \times \text{页大小} + \text{页内偏移} $$ 例如,引用[2]中的示例:逻辑地址的段号和偏移转换为线性地址后,最终通过页机制得到物理地址491674字节。 总结转换关系流程图: ``` 逻辑地址 (段标识符 + 偏移) → [分段机制] → 线性地址 → [分页机制 (如果启用)] → 物理地址 ``` 如果分页未启用,线性地址就是物理地址[^3][^4]。 #### 3. **关键关系和特点** - **层级关系**:逻辑地址是应用程序可见的最低层地址,物理地址是硬件层的最高层地址。线性地址作为中间层,使内存管理更灵活(如支持虚拟内存)[^1][^4]。 - **透明性**:应用程序员(如C开发者)只需处理逻辑地址,分段和分页机制由操作系统(系统程序员)处理,对用户透明。这确保了程序的可移植性和安全性(防止非法内存访问)[^1][^3]。 - **模式依赖**:在实模式下(如早期DOS系统),逻辑地址直接映射到物理地址;在保护模式下(现代OS),必须通过分段和分页转换[^1][^4]。 - **性能影响**:转换过程涉及内存访问(如读取页表),可能引入延迟,因此硬件支持(如TLB缓存)用于优化速度[^4]。 #### 4. **简单示例** 假设一个32位系统: ``` - 逻辑地址:段号=2(段基地址为480 KB),段内偏移=154字节。 - 第一步:计算线性地址 = $480 \times 1024 + 154 = 491674$ 字节(如果分页未启用,这就是物理地址)。 - 第二步:如果分页启用(页大小假设为4 KB): - 线性地址491674字节分解:页号 = $491674 \div 4096 \approx 120$,页内偏移 = $491674 \mod 4096 = 154$。 - 通过页表查得物理页帧号(假设为100),物理地址 = $100 \times 4096 + 154 = 409754$ 字节[^2][^3]. ``` 总之,逻辑地址是程序视角的“虚拟”地址,物理地址是硬件视角的“真实”地址,它们通过分段和分页机制进行转换。这种设计支持内存隔离、共享和保护,是现代操作系统的基石[^1][^3][^4]。 ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值