LDT部段描述符表(Local Descriptor Table)
- 本质段描述符表 , 用于定义段描述符
- 与 GDT 类似 , 可以看作 “段描述符的数组”
- 通过定义选择子访问局部段描述符表中的元素
局部段描述符选择子
与全局段描述符差距在于第二位是不同的
局部段描述符表
特殊寄存器存储ldt_ptr
注意事项:
- 局部段描述符表(它也是一段内存)需要在全局段描述符表中注册( 增加描述项 )
- 通过对应的选择子加载局部段描述符( lldt load local descriptor table)
- 局部段描述符表从第0项(GDT默认第一项占位不使用)开始使用( different from GDT )
LDT的意义:
- 代码层面的意义:分级管理功能相同意义不同的段,减少GDT描述项的负担( 如 : 多个代码段 )
- 系统层面的意义:实现多任务的基础要素( 每个任务对应一系列不同的段 )
LDT的定义与使用
- 定义独立功能相关的段( 代码段,数据段,栈段 )
- 将上述目标段描述符组成局部段描述符表( LDT )
- 为各个段描述符定义选择子( SA_TIL )
- 在 GDT 中定义 LDT 的段描述符 , 并定义选择子
1 2 3 4 5 6 7 8 9 10
| ; GDT definition ;1.GDT 中定义 LDT 的段描述符 段基址, 段界限, 段属性 TASK_A_LDT_DESC : Descriptor 0, TaskALdtLen - 1, DA_LDT ;2.LDT选择子 TaskALdtSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0 ;这里依然是全局段描述符表因此是SA_TIG
;3. mov ax, TaskALdtSelector ;选择子赋值 lldt ax ;加载局部段描述符表LDT jmp TaskACode32Selector : 0 ;使用局部段描述表的代码段
|
局部段的具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| ;====================================== ; ; Task A Code Segment ;====================================== [section .tast-a-ldt] ;Task A局部段描述符表 ; 段基址, 段界限, 段属性 TASK_A_LDT_ENTRY: TASK_A_CODE32_DESC : Descriptor 0, TaskACode32SegLen-1, DA_C + DA_32 TASK_A_DATA32_DESC : Descriptor 0, TaskAData32SegLen-1, DA_DR + DA_32 TASK_A_STACK32_DESC : Descriptor 0, TaskAStack32SegLen-1, DA_DRW + DA_32
TaskALdtLen equ $ - TASK_A_LDT_ENTRY
; Task A LDT Selector TaskACode32Selector equ (0x0000 << 3) + SA_TIL + SA_RPL0 TaskAData32Selector equ (0x0001 << 3) + SA_TIL + SA_RPL0 TaskAStack32Selector equ (0x0002 << 3) + SA_TIL + SA_RPL0
[section .task-a-dat] [bits 32] TASK_A_DATA32_SEGMENT: TASK_A_STRING db "This is Task A!", 0 TASK_A_STRING_OFFSET equ TASK_A_STRING - $$ TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT
[section .task-a-gs] [bits 32] TASK_A_STACK32_SEGMENT: times 1024 db 0 TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT TaskATopOfStack32 equ TaskAStack32SegLen - 1
[section .task-a-s32] [bits 32] TASK_A_CODE32_SEGMENT: mov ax, VideoSelector mov gs, ax mov ax, TaskAStack32Selector mov ss, ax mov eax, TaskATopOfStack32 mov esp, eax mov ax, TaskAData32Selector mov ds, ax
mov ebp, TASK_A_STRING_OFFSET mov bx, 0x0C mov dh, 14 mov dl, 29 call TaskAPrintString jmp Code16Selector : 0
; ds:ebp --> string address ; bx --> attribute ; dx --> dh : row, dl : col TaskAPrintString: push ebp push eax push edi push cx push dx task_print: mov cl, [ds:ebp] cmp cl, 0 je task_end mov eax, 80 mul dh add al, dl shl eax, 1 mov edi, eax mov ah, bl mov al, cl mov [gs:edi], ax inc ebp inc dl jmp task_print
task_end: pop dx pop cx pop edi pop eax pop ebp ret
TaskACode32SegLen equ $ - TASK_A_CODE32_SEGMENT
|
注意这里的打印函数是TaskAPrintString,而不是另一个隶属于.32代码段的printstring函数,
这里选择子已经不同了,即使偏移地址相同,强行调用printstring肯定是要出异常的,因此在此代码段复制粘贴了printstring函数,稍微改个名字就可以使用了。
多任务程序设计思路
待解决的问题:
保护模式下的不同段之间如何进行代码复用(如:调用同一个函数)?
小结
- 局部段描述表用于组织功能相关的段( section )
- 局部段描述符表需要加载后才能正常使用( lldt )
- 局部段描述符表必须在全局段描述符表中注册( Descriptor )
- 通过局部段描述符表的选择子对其进行访问
- 局部段描述符表是实现多任务的基础