内存寻址:逻辑地址到物理地址的转化

 

 

  在计算机里,内存地址分为虚拟内存地址和物理内存地址。linux

数据存放在物理内存中,程序运行时使用的是虚拟内存,并经过虚拟内存地址访问数据和代码。程序员

那操做系统是如何将虚拟内存映射为物理内存地址呢?数据结构

一 虚拟内存布局布局

  以X86的32位系统位例,在32系统中,系统的虚拟内存地址范围为4GB。低2GB给应用程序(Ring3级别)使用。测试

高2GB给系统内核(Ring0)使用。spa

  每一个程序都有属于本身的一个私有2GB虚拟内存空间,以下图:操作系统

其中的0000-ffff前64KB地址为NULL地址区域,0x0000ffff-0x7fff0000为进程空间,3d

0x7fff0000-0x7fffffff为非法区域,禁止程序访问,禁止Ring3级别的应用程序不当心访问到后面2GB的内核内存空间。  指针

 

      

  ① X86的虚拟内存布局 blog

X86支持32位寻址,所以能够支持2的32次方即4GB的虚拟内存空间。虚拟地址再经过页表机制映射到物理地址以便存取物理内存中的数据和指令。

PS: 也能够经过PAE模式将寻址空间扩大到64GB,PAE是物理地址扩展,在4GB的虚拟地址空间中,Windows系统的

 4GB内存主要分为2GB的内核空间和2GB的应用层空间。

  ② X64的虚拟内存布局

X64(AMD64)的内存布局与X86的内存布局相似,不一样的地方在于各自空间的范围和大小不一样,同时X64下还存在一些空洞。

在X64系统,理论上支持最大2的64次方字节寻址空间,空间太大了根本用不完。因此实际上X64系统通常只支持到40多位

Windows支持44位最大寻址空间位16TB,Linux支持48位最大寻址空间位256TB。这些TB的空间并不都是可用的,存在所谓的空洞。

二 逻辑地址转化为物理地址

  程序在执行时,传递给CPU的地址是逻辑地址,逻辑地址由二部分组成,一部分是段选择符(好比cs和ds等段寄存器的值),

另外一部分为有效地址(即偏移地址,好比eip寄存器的值)。

  逻辑地址必须通过转化变成线性地址,线性地址再通过一次映射转为物理地址,才能访问真正的物理内存。

  

①逻辑地址转化为线性地址

逻辑地址是以“段地址:偏移地址”的形式存在的。

段寄存器是一个16位的寄存器,第0位和第1位控制着将要访问段的特权级,第2位说明是在GDT仍是LDT寻找地址,

高13位做为一个索引值,总共8192个索引。以下图所示,经过寄存器里的索引,能够从段描述符表里找到段的基址。

而后用段的基址加上段内的偏移量,就获得了对应的线性地址。

    

②线性地址转化位物理地址

  由第①步获得线性地址,线性地址分为三部分:页目录索引,页表索引,字节偏移索引。

以下图所示,经过页目录索引的CR3寄存器指定的页目录基址之和,能够查询到对应的页表基址。

再经过页表索引和页表基址之和,能够获得对应的页框地址,页框地址再加上页内字节偏移,就获得了物理地址。

    

 

从C源代码通过编译器编译,连接器连接生成可执行文件以后,接下来程序是怎么执行起来的呢?

接下来,操做系统会将可执行文件加载入内存,CPU将从程序的第一个指令开始执行。在理解CPU是如何执行程序以前,先来看看CPU的构造。CPU主要由运算器、控制器、寄存器组和内部总线等构成。 ALU:运算器是计算机中执行各类算术和逻辑运算操做的部件。运算器由算术逻辑单元(ALU,Arithmetic Logical Unit)、累加器、状态寄存器、通用寄存器组等组成。算术逻辑运算单元(ALU)的基本功能为加、减、乘、除四则运算,与、或、非、异或等逻辑操做,以及移位、求补等操做。计算机运行时,运算器的操做和操做种类由控制器决定。运算器处理的数据来自存储器;处理后的结果数据一般送回存储器,或暂时寄存在运算器中。 CU:控制器是计算机的指挥中心,负责决定执行程序的顺序,给出执行指令时机器各部件须要的操做控制命令。由程序计数器、指令寄存器、指令译码器、时序产生器和操做控制器组成,它是发布命令的“决策机构”,即完成协调和指挥整个计算机系统的操做。控制器从内存中取出一条指令,并指出下一条指令在内存中位置,对指令进行译码或测试,并产生相应的操做控制信号,以便启动规定的动做,指挥并控制CPU、内存和输入/输出设备之间数据流动的方向。 寄存器组用于在指令执行事后存放操做数和中间数据,由运算器完成指令所规定的运算及操做。

1)系统总线

CPU的系统总线包括控制总线,数据总线,地址总线。
数据总线用于传送数据信息。数据总线是双向总线,即它既能够把CPU的数据传送到存储器或I/O接口等其余部件,也能够将其余部件的数据传送到CPU。 地址总线是专门用来传送地址的,因为地址只能从CPU传向外部存储器或I/O端口,因此地址总线老是单向的,这与数据总线不一样。地址总线的位数决定了CPU可直接寻址的内存空间大小,好比8位微机的地址总线为16位,则其最大可寻址空间为216=64KB,16位微型机的地址总线为20位,其可寻址空间为220=1MB。通常来讲,若地址总线为n位,则可寻址空间为2n字节。有的系统中,数据总线和地址总线是复用的,即总线在某些时刻出现的信号表示数据而另外一些时刻表示地址,而有的系统则是分开的。 控制总线用来传送控制信号。控制信号中,有的是微处理器送往存储器和I/O接口电路的,如读/写,中断响应信号等; 也有是其余部件反馈给CPU的,好比:中断申请、复位、总线请求、设备就绪等。所以,控制总线的传送方向由具体控制信号而定, 通常是双向的,控制总线的位数要根据系统的实际控制须要而定。实际上控制总线的具体状况主要取决于CPU。

2)寄存器

CPU的一个重要组成部分就是它的寄存器。 计算机体系结构中经常使用到的寄存器包括如下几类寄存器(以32位X86系统为例):
a) 通用寄存器:EAX,EBX,ECX,EDX
b) 源变址目标变址寄存器:ESI,EDI
c) 栈相关寄存器:SS,ESP,EBP
d) 代码段寄存器,程序指令寄存器:CS,IP
e) 数据段寄存器:DS(常与ESI寄存器结合使用)
f) 附加段寄存器:ES(常与EDI寄存器集合使用)
g) Flag标志寄存器:
ZF 零标志,零标志ZF用来反映运算结果是否为0。若是运算结果为0,则其值为1,不然其值为0;
AF 辅助进位标志,运算过程当中第三位有进位值,置AF=1,不然,AF=0;
PF 奇偶标志,当结果操做数中偶数个"1",置PF=1,不然,PF=0;
SF 符号标志,当结果为负时,SF=1;不然,SF=0。溢出时情形例外;
CF 进位标志,最高有效位产生进位值,例如,执行加法指令时,MSB(最高位)有进位,置CF=1;不然,CF=0;
OF 溢出标志,若操做数结果超出了机器能表示的范围,则产生溢出,置OF=1,不然,OF=0。

在64位系统中,寄存器的表示方法为:
通用寄存器:rax, rbx, rcx, rdx
栈寄存器:rsp, rbp
传递参数的寄存器:rdi, rsi, (rdx, rcx,) r8, r9(arguments)
Scratch寄存器:(rbx,) r12, r13, r14, r15(scratch),便可以随时改写的寄存器

那么,CPU是如何一条条执行程序的指令的呢?以下面CPU执行指令图所示, 首先,CPU中的CS寄存器指向了程序被加载内存以后所在代码段的基址,而IP寄存器指向了下一条程序要执行的指令。 CS中的段基址加上IP寄存器中的值,造成一个线性地址,这个线性地址通过转换,造成物理地址,而后经过地址总线, 在对应的内存地址得到对应的一条指令,再把对应的指令经过数据总线传输到CPU的指令缓冲器中, 而后由指令缓冲器传给指令执行控制器,执行对应的指令。

内存的寻址模式讨论

逻辑地址,线性地址,物理地址1.逻辑地址是编译器生成的,咱们使用在linux环境下,使用C语言指针时,指针的值就是逻辑地址。对于每一个进程而言,他们都有同样的进程地址空间,相似的逻辑地址,甚至极可能相同。逻辑地址由段地址+段内偏移组成2.线性地址是由分段机制将逻辑地址转化而来的,若是没有分段机制做用,那么程序的逻辑地址就是线性地址了。3.物理地址是CPU在地址总线上发出的电平信号,要获得物理地址,必需要将逻辑地址通过分段,分页等机制转化而来。 x86体系结构下,使用的较多的内存寻址模型主要有三种: 1. 实模式扁平模型 real mode flat model 2. 实模式分段模型 real mode segment model 3. 保护模式扁平模型 protected mode flat model实模式和保护模式相对,实模式运行于20位地址总线,保护模式则启用了32位地址总线,地址使用的是虚拟地址,引入了描述符表;虽然两者都引入了段这样一个概念,可是实模式的段是64KB固定大小,只有16个不一样的段,CS,DS等存储的是段的序号。保护模式则引入了GDT和LDT段描述符表的数据结构来定义每一个段。 扁平模型和分段模型相对,区别在于程序的线性地址是共享一个地址空间仍是须要分红多个段,即为多个程序是同时运行在同一个CS,DS的范围内仍是每一个程序都拥有本身的CS,DS:也就是说前者(flat)指令的逻辑地址要造成线性地址,不须要切换CS,DS;后者的逻辑地址,必需要通过段选择子去查找段描述符,切换CS,DS,才能造成线性地址。 实模式分段模型 real mode segment model 在实模式里,20位地址总线,16位的寄存器没法表示,一个基址寄存器+一个段寄存器联合起来则能够表示更大的一个地址空间。因而发明了这种段寄存器左移4位+基址寄存器用以间接寻址。 20根地址线,表示 0x00000 - 0xfffff这个范围的地址(即1M) 而寄存器16位,还有4位怎么办?因而8086CPU将1MB的存储器空间分红许多逻辑段,每一个段最大限制为64KB(为了能让16位寄存器寻址,2^20=2^10*2^10=2^10*2^6*2^4==16*64K), 段地址就是逻辑段在主存中的起始位置。为了能用16位寄存器表示段地址,8086规定段地址必须是模16地址,即为xxxx0H形式,省略低4位0,段地址就能够用16位数据表示,它一般被保存在16位的段寄存器中。存单元距离段起始位置的偏移量简称偏移地址,因为限定每段不超过64KB,因此偏移地址也能够用16位数据表示。物理地址:在1M字节的存储器里,每个存储单元都有一个惟一的20位地址,称为该存储单元的物理地址,把段地址左移4位(由于段地址低4位都是零)再加上偏移地址就造成物理地址。Seg<<4+Offset 对于 8086/8088 运行在实模式的程序,其实就是运行在实模式分段模型中。对于不一样的程序,有不一样的CS,DS值,每一个程序的段起始地址都不一样。对于这样的程序而言,偏移地址16位的特性决定了每一个段只有64KB大小。 实模式扁平模型 real mode flat model 该模式只有在386及更高的处理器中才能出现。80386的实模式,就是指CPU可用的地址线只有20位,能寻址0~1MB的地址空间。注意:80386的实模式并不等同于8086/8088的实模式,后者的实模式其实就是实模式分段模型。扁平模型,意味着咱们这里不使用任何的分段寄存器。(尽管也使用了CS,DS,只是不用程序员去显示地为该寄存器赋值,jmp指令时就已经将CS, DS设置好了) 保护模式扁平模型 protected mode flat model Linux, Window XP/7采用的内存寻址模型,Linux中,段主要分为4种,即为内核代码段,内核数据段,用户代码段,用户数据段。 对于内核代码段和数据段而言,CS,DS的值是0xC00000000,而用户代码和数据段的CS,DS的值是0x00000000 当CPU运行于32位模式时,无论怎样,寄存器和指令均可以寻址整个线性地址空间,因此根本就不须要再去使用基地址。基址能够设为一个统一的值。 

相关文章
相关标签/搜索