分类: LINUX
2013-06-12 23:20:12
Linux有三种地址,分别为逻辑地址、线性地址、物理地址。
逻辑地址又称相对地址。指包含在机器语言指令中用来指定一个操作数或一条指令的地址。程序经过编译后,生成的汇编程序中的地址就是逻辑地址。
一个逻辑地址,由两部分组成:段标识符(segment)和段内偏移量(offset)。
段标识符:是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。 段内偏移量是32位长字段。
段内偏移量:指明了从段开始的地方到实际地址之间的距离。
线程地址又称虚拟地址(virtual address)。在32位CPU架构下,可以表示4G的地址空间,用16进制表示就是从0x00000000到0xFFFFFFFF。也就是,高达 4 294 967 296 个存储器单元。
Linux将4G的线性地址空间分为2部分,0~3G为user space,3G~4G为kernel space。如下图
物理地址:用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。
Linux内核将所有的物理页面划分到3类内存管理区中,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。如下图。
内存控制单元(MMU)通过一种称为分段单元(segmentation unit)的硬件电路把一个逻辑地址转换成线性地址;接着,第二个称为分页单元(paging unit)的硬件电路把线性地址转换成一个物理地址如下图。
16位CPU内部拥有20位的地址线,它的寻址范围就是2的20次方,也就是1M的内存空间。但16位CPU用于存放地址的寄存器(IP,SP……)只有16位,因此只能访问65536个存储单元,即64K。为了能够访问1M的内存空间,CPU就采用了内存分段的管理模式,并在CPU内部加入了段寄存器。16位的CPU把1M的内存空间分为若干个逻辑段,每个逻辑段的要求如下:
由于段地址必须是16的倍数,所以,值一般形式为0Xxxxx0H,即前16位二进制是变化的,后四位固定为0,鉴于这种特征,只保存前16位二进制位来保存整个段基地址,所以每次使用时,要用段寄存器左移补4个0(乘以16)来得到实践的段地址。在确定了某个存储单元所属的段后,只知道了改存储单元所属的范围(段地址+65536),要想确定该内存单元的具体位置,还必须知道该单元在段内的偏移量,有了段地址和偏移量,就能确定内存单元在存储器中的具体位置。
逻辑地址 = 段基地址+段内偏移量
由逻辑地址到物理地址的公式为:物理地址=段寄存器的值*16+逻辑地址
16位CPU有四个段寄存器,CS、DS、SS、ES。
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位包含一些硬件细节
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处理器寄存器中。
代码段描述符:表示这个段描述符代表一个代码段;它可以放在 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中。
分段单元(segmentation unit)执行以下操作:
· 先检查段选择符的TI字段,以决定段描述符保存在哪一个描述符表中。TI字段指明描述符是在GDT中(在这种情况下,分段单元从gdtr寄存器中得到GDT的线性基地址)还是在激活的LDT中(在这种情况下,分段单元从ldtr寄存器中得到LDT的线性基地址)。
· 从段选择符的index字段计算段描述符的地址,index字段的值乘以8(一个段描述符的大小),这个结果与gdtr或ldtr寄存器中的内容相加。
· 把逻辑地址的偏移量与段描述符Base字段的值相加就得到了线性地址。
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段寄存器即可。
页(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线性地址空间),即使一个进程并不使用那个范围内的所有地址。二级模式通过只为进程实际使用的那 些虚拟内存区请求页表来减少内存。
在使用的页目录的物理地址存放在控制寄存器cr3中。线性地址内的Directory字段决定页目录中的目录项,而目录项指向适当的页表。地址的Table字段依次又决定页表中的表项,而表项含有页所在页框的物理地址。Offset字段决定页框内的相对位置。由于它是一个 12位长的字段,故每一页含有4096字节的数据。
每一个进程有它自己的页全局目录和自己的页表集。当发生进程切换时,Linux把cr3控制寄存器的内容保存在前一个执行进程的描述符中,然后把下一个要执行进程的描述符的值装入cr3寄存器中。因此,当新进程重新开始在CPU上执行时,分页单元指向一组正确的页表。
从2.6.11版本开始,采用了四级分页模型,4种页表分别被称作:
· 页全局目录(Page Global Directory)
· 页上级目录(Page Upper Directory)
· 页中间目录(Page Middle Directory)
· 页表(Page Table)
页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每一个页表项指向一个页框。线性地址因此被分成五个部分。如下图没有显示位数,因为每一部分的大小与具体的计算机体系结构有关。