Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1086133
  • 博文数量: 143
  • 博客积分: 969
  • 博客等级: 准尉
  • 技术积分: 1765
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-30 12:09
文章分类

全部博文(143)

文章存档

2023年(4)

2021年(2)

2020年(4)

2019年(4)

2018年(33)

2017年(6)

2016年(13)

2014年(7)

2013年(23)

2012年(33)

2011年(14)

我的朋友

分类: LINUX

2013-06-12 23:20:12

一、地址分类及其关系

Linux有三种地址,分别为逻辑地址、线性地址、物理地址。

1、逻辑地址(logical address)

  逻辑地址又称相对地址指包含在机器语言指令中用来指定一个操作数或一条指令的地址。程序经过编译后,生成的汇编程序中的地址就是逻辑地址。

  一个逻辑地址,由两部分组成:段标识符(segment)和段内偏移量(offset)

   段标识符:是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。 段内偏移量是32位长字段。

   段内偏移量:指明了从段开始的地方到实际地址之间的距离。

2、线性地址(linear address)

   线程地址又称虚拟地址(virtual address)。在32位CPU架构下,可以表示4G的地址空间,用16进制表示就是从0x00000000到0xFFFFFFFF。也就是,高达 4 294 967 296 个存储器单元。

   Linux将4G的线性地址空间分为2部分,0~3G为user space,3G~4G为kernel space。如下图

3、物理地址(physical address)

物理地址:用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。

Linux内核将所有的物理页面划分到3类内存管理区中,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。如下图。

    • ZONE_DMA的范围是0~16M,该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于DMA。
    • ZONE_NORMAL的范围是16M~896M,该区域的物理页面是内核能够直接使用的。
    • ZONE_HIGHMEM的范围是896M~结束,该区域即为高端内存,内核不能直接使用。

4、逻辑地址、线性地址、物理地址三者之间关系

内存控制单元(MMU)通过一种称为分段单元(segmentation unit)的硬件电路把一个逻辑地址转换成线性地址;接着,第二个称为分页单元(paging unit)的硬件电路把线性地址转换成一个物理地址如下图。

二、段式管理

1、基本概念

    16位CPU内部拥有20位的地址线,它的寻址范围就是2的20次方,也就是1M的内存空间。但16位CPU用于存放地址的寄存器(IP,SP……)只有16位,因此只能访问65536个存储单元,即64K。为了能够访问1M的内存空间,CPU就采用了内存分段的管理模式,并在CPU内部加入了段寄存器。16位的CPU把1M的内存空间分为若干个逻辑段,每个逻辑段的要求如下:

  •      逻辑段的起始地址(段地址)必须是16的倍数,即最后4位二进制位必须全为0.
  •      逻辑段最大容量为64k

    由于段地址必须是16的倍数,所以,值一般形式为0Xxxxx0H,即前16位二进制是变化的,后四位固定为0,鉴于这种特征,只保存前16位二进制位来保存整个段基地址,所以每次使用时,要用段寄存器左移补4个0(乘以16)来得到实践的段地址。在确定了某个存储单元所属的段后,只知道了改存储单元所属的范围(段地址+65536),要想确定该内存单元的具体位置,还必须知道该单元在段内的偏移量,有了段地址和偏移量,就能确定内存单元在存储器中的具体位置。

    逻辑地址 = 段基地址+段内偏移量

由逻辑地址到物理地址的公式为:物理地址=段寄存器的值*16+逻辑地址

  16位CPU有四个段寄存器,CS、DS、SS、ES。

  • CS+IP:用于代码段的访问,CS指向存放程序的段基地址,IP指向下条要执行的指令在CS段的偏移量,用这两个寄存器就可以得到一个内存的物理地址,该地址存放着一条要执行的指令。
  • SS+SP:用于堆栈段的访问,SS指向堆栈段的基地址,SP指向栈顶,用这两个寄存器就可以直接访问栈顶单元的内存物理位置。
  • DS+BX:用于数据段的访问,DS中的值左移四位得到数据段的起始地址,在加上BX中偏移量,得到一个存储单元的物理地址。
  • ES+BX:用于附加段的访问,ES中的值左移四位得到附加段的起始地址,在加上BX中偏移量,得到一个存储单元的物理地址。

32位CPU的内存管理仍然采用分段的管理模式,逻辑地址同样由段内地址和偏移量组成,32位CPU采用了两种不同的工作方式:实模式和保护模式。

1、实模式

  在实模式下,32位CPU的内存管理与16位CPU是一致的。

2、保护模式

  段基址长达32位,每个段的最大容量达4G,段寄存器的值是段地址的选择器(逻辑地址,由段标识符和段内偏移量,在32位中把段标识符也称段选择符),用它从内存中得到一个32位的段地址,存储单元的物理地址就是该段地址加上段内偏移量,这与32位CPU的物理地址计算方式完全不同。

这里涉及到2个名词:段寄存器和段选择符

也就是说,段寄存器,是用来存储段基地址的,而这个段基地址,又称为段选择符。

段寄存器:32位CPU有6个段寄存器,除了16位中提到到CS,DS,SS,ES,还有FS和GS。

段选择符:前13位表示一个索引号,后3位包含一些硬件细节

image

index :指定了放在 GDT 或 LDT 中的相应段描述符的入口(在下面将做进一步的讲述)。
TI((Table Indicator)标志:指明段描述符是在 GDT 中(TI=0)或在 LDT 中(TI=1)。

RPL 请求者特权级:当相应的段选择符装入到 cs 寄存器中时指示出 CPU 当前的特权级

段描述符:每个段由一个8字节的段描述符(Segment Descriptor)表示(参见下图),它描述了段的特征。段描述符放在全局描述符表(Global Descriptor Table, GDT)                 或局部描述符表(Local Descriptor Table, LDT)中。通常只定义一个GDT,而每个进程除了存放在GDT中的段之外如果还需要创建附加的段,就可以有自己                 的LDT。GDT在主存中的地址和大小存放在gdtr处理器寄存器中,当前正被使用的LDT地址和大小放在ldtr处理器寄存器中。

image

代码段描述符:表示这个段描述符代表一个代码段;它可以放在 GDT 或 LDT 中。该描述符置 S 标志(非系统段)。
数据段描述符:表示这个段描述符代表一个数据段,它可以放在GDT或LDT中。该描述符置S标志。栈段是通过通用的数据段实现的。
任务状态段描述符(TSSD):表示这个段描述符代表一个任务状态段(Task State Segment,TSS),也就是说这个段用于保存处理器寄存器的内容。它只能出现在GDT中。根据相应的进程是否正在CPU上运行,其Type字段的值分别为11或9。这个描述符的S标志置为0。
局部描述符表描述符(LDTD):表示这个段描述符代表一个包含 LDT的段,它只出现在GDT中,相应的Type字段为2,S标志为0。下一节说明80x86处理器是如何决定一个段描述符是存放在GDT中还是存放在进程的LDT中。

2、逻辑地址到线性地址转换

分段单元(segmentation unit)执行以下操作:
· 先检查段选择符的TI字段,以决定段描述符保存在哪一个描述符表中。TI字段指明描述符是在GDT中(在这种情况下,分段单元从gdtr寄存器中得到GDT的线性基地址)还是在激活的LDT中(在这种情况下,分段单元从ldtr寄存器中得到LDT的线性基地址)。
· 从段选择符的index字段计算段描述符的地址,index字段的值乘以8(一个段描述符的大小),这个结果与gdtr或ldtr寄存器中的内容相加。
· 把逻辑地址的偏移量与段描述符Base字段的值相加就得到了线性地址。

image

 

3、Linux中的分段

Linux下逻辑地址与线性地址是一致的,即逻辑地址的偏移量字段的值与相应的线性地址的值总是一致的。

Linux将地址分为用户态和内核态:

运行在用户态的所有Linux进程都使用一对相同的段来对指令和数据寻址。这两个段就是所谓的用户代码段和用户数据段。类似地,运行在内核态的所有Linux进程都使用一对相同的段对指令和数据寻址:它们分别叫做内核代码段和内核数据段。

四个主要的Linux段的段描述符字段的值
段              Base              G Limit  S Type DPL D/B P
用户代码段0x00000000 1 0xfffff 1 10    3     1    1
用户数据段0x00000000 1 0xfffff 1 2      3     1    1
内核代码段0x00000000 1 0xfffff 1 10    0     1    1
内核数据段0x00000000 1 0xfffff 1 2      0     1    1

相应的段描述符由宏__USER_CS,__USER_DS,__KERNEL_CS,和__KERNEL_DS分别定义。例如,为了对内核代码段寻址,内核只需要把这个宏产生的值装进cs段寄存器即可。

 

三、分页管理

1、基本概念

   页(page): 线性地址被分成固定长度的单位组,每一个单元就是一个页。如32位的机器,线性地址最大为4G,如果用4KB为一个页来划分,整个线性地址就划分为                      2的20次方个

  页框(page frame):也叫物理页,分页单元把所有的物理内存划分为固定长度的单位组,它的长度一般与线性地址页是相同的。每一个页框包含一个页。

  页与页框的区别:

   a)页是线性地址(虚拟地址)划分的而来的,而页框是物理内存划分而来的。

   b)页是一个数据块,可以存放在页框或者磁盘中。页框是一个存储区域。

32位的线性地址被分成3个字段:

目录(Directory)   页表(Table)        偏移量(Offset)
最高10位 中间10位 最低12位

页目录表(page directory):最高10位 称位页目录表(page directory)

页表(page table):中间10位称为页表(page table)

 

这种二级模式的目的在于减少每个进程页表所需RAM的数量。如果使用简单的一级页表,那将需要高 达220个表项(也就是,在每项4个字节时,需要4M RAM)来表示每个进程的页表(如果进程使用全部 4GB线性地址空间),即使一个进程并不使用那个范围内的所有地址。二级模式通过只为进程实际使用的那 些虚拟内存区请求页表来减少内存。

2、线性地址到物理地址转换

image

  在使用的页目录的物理地址存放在控制寄存器cr3中。线性地址内的Directory字段决定页目录中的目录项,而目录项指向适当的页表。地址的Table字段依次又决定页表中的表项,而表项含有页所在页框的物理地址。Offset字段决定页框内的相对位置。由于它是一个 12位长的字段,故每一页含有4096字节的数据。

   每一个进程有它自己的页全局目录和自己的页表集。当发生进程切换时,Linux把cr3控制寄存器的内容保存在前一个执行进程的描述符中,然后把下一个要执行进程的描述符的值装入cr3寄存器中。因此,当新进程重新开始在CPU上执行时,分页单元指向一组正确的页表。

3、Linux中的分页

从2.6.11版本开始,采用了四级分页模型,4种页表分别被称作:
· 页全局目录(Page Global Directory)
· 页上级目录(Page Upper Directory)
· 页中间目录(Page Middle Directory)
· 页表(Page Table)
页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每一个页表项指向一个页框。线性地址因此被分成五个部分。如下图没有显示位数,因为每一部分的大小与具体的计算机体系结构有关。

image


说明:
图引用自http://www.cnblogs.com/zszmhd/archive/2012/08/29/2661461.html



阅读(2161) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~