一直以来,我们把所有的段描述符都放在GDT中,而不管它属于内核还是用户程序,为了有效地在任务之间实施隔离,处理器建议每个任务都应当具有自己的描述符表,称为局部描述符表LDT,并且把专属于自己的那些段放到LDT中。
和GDT一样,LDT也是用来存放描述符的。不同之处在于,LDT只属于某个任务。或者说,每个任务都有自己的LDT,每个任务私有的段,都应当在LDT中进行描述。另外,LDT的第1个描述符,也就是0号槽位,也是有效的、可以使用的。
[SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
LABEL_DESC_LDT: Descriptor 0, LDTLen - 1, DA_LDT; LDT
; GDT 结束GdtLenequ$ - LABEL_GDT; GDT长度
GdtPtrdwGdtLen - 1; GDT界限
dd0; GDT基地址; GDT 选择子
SelectorCode32equLABEL_DESC_CODE32- LABEL_GDT
SelectorVideoequLABEL_DESC_VIDEO- LABEL_GDT
SelectorLDTequLABEL_DESC_LDT- LABEL_GDT[SECTION .s16]
[BITS16]
LABEL_BEGIN:
movax, cs
movds, ax
moves, ax
movss, ax
movsp, 0100h; 初始化 32 位代码段描述符
xoreax, eax
movax, cs
shleax, 4
addeax, LABEL_SEG_CODE32
movword [LABEL_DESC_CODE32 + 2], ax
shreax, 16
movbyte [LABEL_DESC_CODE32 + 4], al
movbyte [LABEL_DESC_CODE32 + 7], ah; 初始化 LDT 在 GDT 中的描述符
xoreax, eax
movax, ds
shleax, 4
addeax, LABEL_LDT
movword [LABEL_DESC_LDT + 2], ax
shreax, 16
movbyte [LABEL_DESC_LDT + 4], al
movbyte [LABEL_DESC_LDT + 7], ah; 初始化 LDT 中的描述符
xoreax, eax
movax, ds
shleax, 4
addeax, LABEL_CODE_A
movword [LABEL_LDT_DESC_CODEA + 2], ax
shreax, 16
movbyte [LABEL_LDT_DESC_CODEA + 4], al
movbyte [LABEL_LDT_DESC_CODEA + 7], ah; 为加载 GDTR 作准备
xoreax, eax
movax, ds
shleax, 4
addeax, LABEL_GDT; eax <- gdt 基地址
movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 加载 GDTR
lgdt[GdtPtr]; 关中断
cli; 打开地址线A20
inal, 92h
oral, 00000010b
out92h, al; 准备切换到保护模式
moveax, cr0
oreax, 1
movcr0, eax; 真正进入保护模式
jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs,
; 并跳转到 SelectorCode32:0 处
; END of [SECTION .s16]
GDTR全局描述符寄存器:48位,高32位存放GDT基址,低16为存放GDT限长。
LDTR局部描述符寄存器:16位,高13为存放LDT在GDT中的索引值。
LDTR寄存器只用于指向当前任务的LDT。每当发生任务切换时,LDTR的内容被更新以指向新任务的LDT。如果选择子的TI被置为1则系统将从当前LDT中寻找相应描述符。当用到SelectorLDTCodeA时,系统会从LDT中找到LABEL_LDT_DESC_CODEA描述符,并跳转到相应的段中。在用LDT前需要先用lldt指令加载ldtr,lldt的操作数是GDT中用来描述LDT的描述符。
; LDT
[SECTION .ldt]
ALIGN32
LABEL_LDT:
; 段基址 段界限 属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位LDTLenequ$ - LABEL_LDT; LDT 选择子,SA_TIL将此选择子的TI位置为1.
SelectorLDTCodeAequLABEL_LDT_DESC_CODEA- LABEL_LDT + SA_TIL
; END of [SECTION .ldt]
使用方法如下:
LABEL_SEG_CODE32:
...
; Load LDT
movax, SelectorLDT
lldtaxjmpSelectorLDTCodeA:0; 跳入局部任务
下面是最终运行的代码块:
; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN32
[BITS32]
LABEL_CODE_A:
movax, SelectorVideo
movgs, ax; 视频段选择子(目的)movedi, (80 * 12 + 0) * 2; 屏幕第 10 行, 第 0 列。
movah, 0Ch; 0000: 黑底 1100: 红字
moval, 'L'
mov[gs:edi], axjmp$
CodeALenequ$ - LABEL_CODE_A
; END of [SECTION .la]
运行结果如下:
【源码及软盘映像】