历史
许多年以前,当人们还在使用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位的地址,那么对应的地址空间有多大呢?共1M,这在8086年代已经很大啦,已经可以满足用户需求啦。
在 80386(也就是大名鼎鼎的奔3)中,增加了2个段寄存器,共有6个16位的段寄存器,CS、DS、SS、ES、FS、GS(后面3个是附加数据段寄存器)。但是,这些段寄存器中存放的不再是某个段的基地址,而是某个段的选择符(Selector)。之所以要进行转变,是因为(1)16位的寄存器无法存放32位的段基地址;(2)内存变大,远远超出了1M的大小。但是寻址还是要找到段基地址的,段寄存器既然放不了了,那怎么办呢?
段基地址只好存放在一个叫做描述符表(Descriptor)的表中。因此,在80386中,我们把段寄存器叫做选择符(子),这些段寄存器里面放的不再是基址,而是下标及其他控制信息(为什么不把段寄存器里面的内容全当成下标呢?那会有下标,实际上,描述符表并没有这么长,同时描述符表分成了全局描述符表GDT和局部描述符表LDT,且为了安全控制,要将“用户”进行分级控制,不能随便访问数据,所以还要进行用户控制,因此,这里只用了13位作为下标表示,共有8192个,余下3位中的1位表示查哪个描述符表,最后2位表示权限控制,00为系统内核态,11为用户态)。下面给出6个段寄存器的名称和用途:
地址转换
一、逻辑地址转线性地址
机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址才能够被访问到。

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





