局部段描述符表的使用

简介: 局部段描述符表的使用

局部段描述符表基本概念

  • 描述符表有 GDT 和 LDT 两种,在保护模式的代码实现中我们用的都是 GDT
  • 现在我们就来学习一下 LDT
  • LDT 全称: Local Descripter table
  • 同 GDT 一样,LDT 本质上也是段描述符表,可以看成段描述符数组
  • 也是通过选择子的方式来访问局部段描述符表中的元素
  • 局部段描述符和选择子与全局段描述符和选择子格式一致

LDT 与 GDT 使用的差异

  • 局部段描述符表从第 0 项开始使用,而全局段描述符表是从第 1 项开始使用
  • 加载局部段描述符表使用的是 lldt 指令,而加载全局段描述符表使用的是 lgdt 指令
  • 局部段描述符表需要在全局段描述符表中注册(增加描述项)

LDT 的意义

  • 它是 CPU 厂商为在硬件一级原生支持多任务而创造的表,按照 CPU 的设想,一个任务对应一个 LDT
  • CPU 厂商建议每个任务的私有内存段都应该放到自己的段描述符表中,该表就是 LDT
  • LDT 是实现多任务的基本要素
  • 当然,现在可能很难理解 LDT 对多任务的意义,可以暂时先放着,心中有个概念就行,后面再逐步具体体会

LDT 的定义与使用

  • 先上完整代码:loader.asm
  • 参考 GDT 的实现,LDT 实现与 GDT 非常相似
  • 首先是局部段描述符表和局部段选择符的定义
; Task A 局部段描述符表定义,段基址先默认为 0 ,后面填充
TASK_A_LDT_BASE     :
TASK_A_CODE32_DESC  : Descriptor 0, TASK_A_CODE32_SEG_LEN - 1,  DA_C + DA_32
TASK_A_DATA32_DESC  : Descriptor 0, TASK_A_DATA32_SEG_LEN - 1,  DA_DR + DA_32
TASK_A_STACK32_DESC : Descriptor 0, TASK_A_STACK32_SEG_LEN - 1, DA_DRW + DA_32
; ...
TASK_A_LDT_LEN      equ   $ - TASK_A_LDT_BASE  ; 长度 = 当前地址 - TASK_A_LDT_BASE 地址
; Task A 局部段选择符定义,RPL = 0; TI = 1
TASK_A_CODE32_SELECTOR    equ   (0x0000 << 3) + SA_RPL0 + SA_TIL
TASK_A_DATA32_SELECTOR    equ   (0x0001 << 3) + SA_RPL0 + SA_TIL
TASK_A_STACK32_SELECTOR   equ   (0x0002 << 3) + SA_RPL0 + SA_TIL
  • 注意 lldt 指令加载的是选择子,与 lgdt 不同
mov ax, TASK_A_LDT_SELECTOR
lldt ax
  • 别忘了段基址需要填充
CODE16_START
  ...
  mov esi, TASK_A_LDT_BASE
  mov edi, TASK_A_LDT_DESC 
  call InitDescItem
  mov esi, TASK_A_CODE32_SEGMENT
  mov edi, TASK_A_CODE32_DESC
  call InitDescItem
  mov esi, TASK_A_DATA32_SEGMENT
  mov edi, TASK_A_DATA32_DESC
  call InitDescItem
  mov esi, TASK_A_STACK32_SEGMENT
  mov edi, TASK_A_STACK32_DESC 
  call InitDescItem
  • 注意,局部段描述符表需要在全局段描述符表中注册(增加描述项),即把局部段描述符表当做一个段,然后给这个段定义一个全局描述符和选择符
; 全局描述符表定义
...
TASK_A_LDT_DESC : Descriptor 0, TASK_A_LDT_LEN-1, DA_LDT
; 全局段选择符定义,RPL = 0; TI = 0
...
TASK_A_LDT_SELECTOR equ (0x0005 << 3) + SA_RPL0 + SA_TIG
  • Task A 的数据段,代码段,栈段实现主体自己查看源码分析。
  • 在原 32 位代码段程序后面添加加载局部段描述符表和跳转到局部代码段执行的代码
mov ax, TASK_A_LDT_SELECTOR
lldt ax
jmp TASK_A_CODE32_SELECTOR:0
  • 于是,程序跳转到 TASK_A_CODE32_SEGMENT 代码处执行
  • TASK_A_CODE32_SEGMENT 处的程序即为局部代码段,实现功能:打印 “Task A” 字符串
  • 注意了:为啥又重新实现了打印函数 TaskAPrintString ,这个函数不是跟 print_str_32 一模一样的吗?
  • 因为 TaskAPrintString 和 print_str_32 不属于同一个代码段,段界限规定了每个程序段的范围,A 程序当然无法调用 B 程序中的代码。这正体现了保护模式的保护二字
  • 最后看一下代码的运行效果

  • 问题:TaskAPrintString 和 print_str_32 实现的功能一模一样,那么,如何实现一个函数,可供不同的代码段调用呢?
  • 请看下一章节讲解
目录
相关文章
|
2月前
|
JavaScript 前端开发
理解局部作用域
【9月更文挑战第03天】
29 2
|
4月前
|
机器学习/深度学习 资源调度 Java
【YOLOv8改进 - 注意力机制】GC Block: 全局上下文块,高效捕获特征图中的全局依赖关系
YOLOv8专栏探讨了目标检测的创新改进,如整合NLNet和SENet优势的GCBlock,用于高效全局上下文建模。GCNet在多个识别任务中表现优越,同时降低了计算成本。文章提供了论文、代码链接及详细实现,包括特征的全局建模、变换和融合步骤。核心GCBlock代码展示了其结构。更多实战案例和配置见相关链接。
|
C语言
C 中的变量作用域 – 局部和全局作用域解释
C 中的变量作用域 – 局部和全局作用域解释
13:分段函数
13:分段函数
115 0
|
安全 PHP 开发者
全局空间|学习笔记
快速学习全局空间,了解全局空间的概念,掌握全局空间与其他命名空间的关系以及合作模式。
全局空间|学习笔记
|
机器学习/深度学习 存储 Linux
内存为什么要分段? 分成多少种段? 段与段寄存器的区别?
内存为什么要分段? 分成多少种段? 段与段寄存器的区别?
|
C++ 机器学习/深度学习