IA-32 CPU的分段机制和分页机制构成存储管理单元,并从硬件上支持存储管理。
在保护方式下,IA-32处理器必须使用分段机制,无法禁止。
IA-32处理器通过分段机制由逻辑地址获得32位线性地址 ,如不采用分页机制,则32位线性地址就是32位物理地址 ; 如果允许分页,则32位线性地址就是虚拟地址 ,由分页机制转换成32位物理地址 。
段式管理:
一个程序常包含多个段....代码,数据,堆栈等
每个段需要:段基地址,段界限,(访问权限,描述符)
并且,段表本身也是一个特殊的段,由操作系统维护....(GLTR存储段表基址及长度)
由逻辑地址到线性地址的转换过程为:
逻辑地址 (由段选择器及偏移地址32位组成)------>线性地址...
//实际指令执行时,一条指令若要取某个操作数时,由偏移地址,段选择器名字(可能2^3即由三个bit标识)来得到段寄存器(ds,es,ss),然后由这个段寄存器查找相应的段描述符。??
段选择器(即找到的寄存器??) :由高13位索引域及TI(1位)、RPL(2位,特权级别)组成.. TI为0则寻找GDT,TI为1寻找LDT。 --》LD其实也在GDT中..
RPL为本次请求的特权级别。。。。。 意思是与GD中DPL还有些不同....
段描述符 :共8个字节,基址(4个字节),界限(20位),访问权(1字节),其它属性共4个字节。-->由高字节到低字节依次为:
[基址31-24][G,D/B,0,AVL,界限19-16] 6
[访问权字节][基址23-16 ] 4
[基地址23-0位 ] 2
[界限15-0位 ] 0
G:粒度,段界限是1B还是4MB,如为4MB,则界限可达2^20*4M=2^32B,即4G的段长度。
D/B在可执行代码段中时为D位:指明默认为16位操作数和寻址方式(0),或32位操作数和寻址方式(1)
D/B:在向下扩展的数段的描述符中,为B位。指明上限为4GB(1)或64KB(0)。???
D/B:在描述堆栈段(由SS指向段),为B位。 B=1,隐式堆栈访问指令(push,pop,call)使用32位esp, B=0时用sp..
AVL,不使用,可给操作系统使用....
访问权限字节有:[P,DPL两bit,S,TYPE四bit]
P:是否在内存中,存在位.
DPL:描述符特权等级0~3
S:是数据/代码段(s=1)还是系统段/门段(s=0)描述符.......
TYPE: 4bit故有16种状态。。???
当为数据段类型时0-7:
0 只读, 1 只读、已访问, 2 读/写, 3 读/写、已访问, 4 只读、向下扩展, 5 只读、向下扩展、已访问 , 6 读/写、向下扩展, 7 读/写、向下扩展、已访问
当为代码段时8-F:相对于数据段,执行对应于读,读对应于写,一致代码段对应于向下扩展。
当为系统段时:???
1 可用286TSS,2 LDT(说明是局部描述符表),3 忙的286TSS(说明正在执行或扶起)
4 286调用门(?), 5 任务门(?), 6 286中断门(?), 286陷阱门(?)
9 可用386TSS, B 忙的386TSS, C 386调用门, E 386中断门, F 386陷阱门.
一致代码段中一致的意思是:
当转移目标的一致代码段特权更高时,当前的特权级会被延续下去。
当 非 ,会引起常规保护错误,除非使用调用门或任务门。??
并且,如果目标代码的特权级低,无论一致与否,都不能call或jmp进去.....
并且,所有数据段都是非一致的,故不可能被低特权级的代码访问到。。。。。与代码段不同的是,数据段可被更高特权的代码访问到,而不用特定的门。。。
这也就是说 :
对于非一致代码,只有相同特权级之间可访问....
对于一致代码,只有特权级高的不能访问低的。。。。。。
对于数据段(总是非一致):只有特权级低的不能访问高的,同级以及向下都是可访问的....
//故对于一个打分程序,学生的可在用户态,老师的程序可在核心态,老师可改学生的,而学生则。。。
实现:
; 在下列类型值命名中:
; DA_ : Descriptor Attribute
; D : 数据段
; C : 代码段
; S : 系统段
; R : 只读
; RW : 读写
; A : 已访问
............................... ....................................................................................常量定义好像有问题!!!!
; 描述符类型
DA_32 EQU 4000h ; 32 位段
DA_DPL0 EQU 00h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
; 存储段描述符类型
DA_DR EQU 90h ; 存在的只读数据段类型值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值
DA_C EQU 98h ; 存在的只执行代码段属性值
DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值
DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值
DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值
; 系统段描述符类型
DA_LDT EQU 82h ; 局部描述符表段类型值
DA_TaskGate EQU 85h ; 任务门类型值
DA_386TSS EQU 89h ; 可用 386 任务状态段类型值
DA_386CGate EQU 8Ch ; 386 调用门类型值
DA_386IGate EQU 8Eh ; 386 中断门类型值
DA_386TGate EQU 8Fh ; 386 陷阱门类型值
段描述符的宏定义如下:
参数1:基址
参数2:段界限
参数3:属性
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限1 (0-15)
dw %1 & 0FFFFh ; 段基址1 (0-15)
db (%1 >> 16) & 0FFh ; 段基址2 (16-23)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2 界限为:16-19,把第访问权字节和G,D,AVL统一处理.
db (%1 >> 24) & 0FFh ; 段基址3 (24-31)
%endmacro ; 共 8 字节
l
另关于LDT....LDT在系统中可有多个,但是每个任务最多一个。。。 引用如下:
(Linux 只用到 GDT,而没有使用LDT。。。).
GDT 可以被放在内存的任何位置,那么当程序员通过段寄存器来引用一个段描述符时,CPU必须知道GDT的入口,也就是基地址放在哪里,所以 Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口 地址装入此积存器,从此以后,CPU就根据此积存器中的内容作为GDT的入口来访问GDT了。
GDT是Protected Mode所必须的数据结构,也是唯一的——不应该,也不可能有多个。另外,正象它的名字(Global Descriptor Table)所揭示的,它是全局可见的,对任何一个任务而言都是这样。
除 了GDT之外,IA-32还允许程序员构建与GDT类似的数据结构,它们被称作LDT(Local Descriptor Table),但与GDT不同的是,LDT在系统中可以存在多个,并且从LDT的名字可以得知,LDT不是全局可见的,它们只对引用它们的任务可见,每个 任务最多可以拥有一个LDT。另外,每一个LDT自身作为一个段存在,它们的段描述符被放在GDT中。
IA-32为LDT的入口地址也提供了 一个寄存器LDTR,因为在任何时刻只能有一个任务在运行,所以LDT寄存器全局也只需要有一个。如果一个任务拥有自身的LDT,那么当它需要引用自身的 LDT时,它需要通过LLDT将其LDT的段描述符装入此寄存器。LLDT指令与LGDT指令不同的时,LGDT指令的操作数是一个32-bit的内存地 址,这个内存地址处存放的是一个32-bit GDT的入口地址,以及16-bit的GDT Limit。而LLDT指令的操作数是一个16-bit的选择子,这个选择子主要内容是:被装入的LDT的段描述符在GDT中的索引值——这一点和刚才所 讨论的通过段积存器引用段的模式是一样的。