GDT LDT IDT TSS
GDT LDT IDT TSS
和一个段有关的信息需要8个字节来描述,所以称之为段描述符(Segment Descriptor),每个段都需要一个描述符。为了存放这些描述符,需要在内存中开辟出一段空间。在这段空间中,所有的描述符都是挨在一起的,集中存放的,这就构成了描述符表。
i386中,除了段描述符,还有一种描述符叫做“门描述符”,描述控制转移的入口点,也就是异常控制中断的入口点,通过这种门可以实现特权级的转变和任务的切换。
GDT & GDTR
GDT : Global Descriptor Table , 全局描述符表,用来存放全局的段描述符。
GDTR : Global Descriptor Table Register, 用来保存全局描述符表(GDT)的起始地址。
整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。
- 全局描述符表线性基地址:保存的是全局描述符表在内存中的起始线性地址;
- 16位的全局描述符表边界:在数值上等于表的大小(总字节数)减一,16位决定了表最大65536字节(64KB),又因为每个描述符占8个字节,所以最多能有8192个描述符;
**段选择子(Selector)**由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)。
- 描述符索引 : 指示段描述符在GDT中的下标;
- TI : 描述符表指示器(Table Indicator)0表示此描述符在GDT中,1表示此描述符在LDT中;
- RPL:请求特权级,表示给出当前选择子的那个程序的特权级;
G :粒度(Granularity)位,解释段界限的含义,0表示段界限以字节为单位;1表示段界限以4KB为单位;
S : 类型(Descriptor Type)位,0表示是一个系统段;1表示是一个代码段或者数据段;
DPL : 表示描述符的特权级( Descriptor Privilege Level)。 对应CPU的0,1,2,3特权级,数字越小特权级越高;
P : 段存在(Segment Present)位,描述此段是否在内存中,在为1,不在为0;
D/B:默认的操作数大小,如果代码段的D位为0,那么处理器在这个段上执行时,将使用16位指令指针寄存器IP来取指令,否则用32位的取指令;对于栈段,此位为B位,用于在进行隐式的栈操作时,是使用SP寄存器还是ESP寄存器,如果该位为0,使用SP,否则使用ESP;
L: 64位代码段标志,保留给64位系统使用,否则填0;
TYPE段共4位,用于指示描述符的子类型,X表示是否可执行。E表示数据段的扩展方向,E=0表示向上扩展,也就是向高地址方向扩展,即普通数据段;E=1表示向下扩展,即栈段;W=0表示不可写入;C=0表示非依从代码段,这样的代码段可以从与它特权级相同的代码段调用;C=1表示允许从低特权级的程序转移到该段执行。
LDT & LDTR
为了有效地在任务之间实施隔离,处理器建议每个任务都应该具有自己的描述符表,称之为局部描述符表LDT(Local Descriptor Table),并且将属于自己的段放在LDT中。
局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。
为了追踪和访问这些LDT,处理器使用了局部描述符表寄存器LDTR。在一个多任务系统中,会有很多任务在轮流执行,正在执行的那个任务,称为当前任务(Current Task),因为LDTR只有一个,所以,它只指向当前任务的LDT。每当发生任务切换时,LDTR的内容会被更新。
IDT & IDTR
中断描述符表(Interrupt Descriptor Table, IDT)保存的是和中断过程有关的描述符,包括中断门、陷阱门和任务门,整个系统中只有一个IDT。
中断描述符表寄存器(Interrupt Descriptor Table Register,IDTR)保存着中断描述符表在内存中的线性基地址和界限。
中断描述符表的每一行是一个门描述符。
门描述符:中断门(Interrupt Gate),陷阱门(Trap Gate) 任务门(Task Gate)和调用门(Interrupt Gate),其中中断门和陷阱门必须存放在IDT中,任务门和调用门可以存储在GDT\LDT和IDT中。除了任务门外其他三种门的结构基本相同, 中断门,陷阱门,调用门结构如下图所示。任务门结构在下一小节TSS&TR
后边有贴图。
中断i发生 ->确定与中断或异常关联的向量i-> 通过查询IDT的第i项目 -> 得到中断描述符(门)-> 得到段选择符 -> 查询GDT - >得到段描述符 -> 得到段的基地址 -> 段基地址加上中断描述符中的偏移地址 -> 得到中断服务程序的入口地址。 过程中或检查特权级,如果特权级发生了变化还会发生栈切换。
中断门和陷阱门的唯一区别在于,通过中断门进入中断服务程序时CPU会自动关闭中断,也就是将CPU中的EFLAGS
寄存器的IF
标志位清成0,即防止中断嵌套。而通过陷阱门进入服务程序时则维持IF
标志位不变。
TSS & TR
任务状态段(Task State Segment : TSS)是一个专门用来保存任务运行现场的数据结构,其中包括CPU中所有与具体进程有关的寄存器的内容(包含页面目录指针CR3),还包括了三个堆栈指针。中断发生时,CPU在中断向量表中找到相应的表项。如果找到的表项是一个任务门,并且通过了优先级别的检查,CPU就会将当前任务的运行现场保存相应的TSS中,并将任务门所指向的TSS作为当前任务,将其内容装入CPU中各个寄存器,从而完成一次任务的切换。
任务寄存器TR用来指向当前任务的TSS。 TR寄存器在处理器中也是只有一个,当任务切换发生时,TR寄存器的内容会跟着指向新的任务的TSS。这个过程是这样的:首先,处理器将当前任务的现场信息保存到由TR寄存器指向的TSS, 然后,再使TR寄存器指向新的TSS,并从新任务的TSS中恢复现场。
进入中断服务函数,或者发生任务切换时,栈也要发生切换,TSS中除了保存有常规的寄存器内容外,还有三个额外的堆栈指针(SS
和ESP
),这三个额外的堆栈指针分别用于当CPU在目标代码段中的运行级别为0,1,2时的栈。图中的SS0
ESP0
SS1
ESP1
等就对应不同级别下的栈。
DPL & CPL & RPL
DPL : Descriptor Privilege Level, 存放在段描述符中,用于表示段的特权级;
CPL : Current Privilege Level, 代表当前执行程序的特权级;
RPL : Request Privilege Level, 请求特权级,存放在段选择子中.
每个段描述符中都有一个DPL
位段,即描述项优先级别位段。当CPU通过中断门找到一个代码段描述符,并进而转入相应的服务程序时,就把这个代码段描述项装入CPU中,而描述项的DPL
就变成CPU的当前运行级别,即CPL
。
而中断门也有一个DPL
,当通过int
指令进入一个中断服务程序时,在指令中给出一个中断向量,CPU先根据该中断向量找到中断门,将中断门的DPL
和当前CPU的CPL
进行对比,CPL
必须小于等于DPL
才能通过该中断门。穿过门后再对比CPU的CPL
和段描述符的DPL
。