任务切换的方法——《x86汇编语言:从实模式到保护模式》读书笔记37

2023-05-16

任务切换的方法——《x86汇编语言:从实模式到保护模式》读书笔记37

1. 中断门和陷阱门

在实模式下,内存最低端的1M是中断向量表,保存着256个中断处理过程的段地址和偏移。当中断发生时,处理器把中断号乘以4,作为索引访问中断向量表,从相应的位置取出中断处理过程的段地址和偏移地址,并转移到哪里执行。具体过程可以参考我的博文
8086中断系统——《x86汇编语言:从实模式到保护模式》读书笔记04

在保护模式下,中断向量表不再使用,取而代之的是中断描述符表(Interrupt Descriptor Table,IDT)。不要害怕,它和GDT、LDT是一样的,用于保存描述符。唯一不同的地方是,它保存的是门描述符,包括中断门、陷阱门和任务门。

中断门和陷阱门的格式如下图:

当中断发生时,处理器用中断号乘以8(因为每个描述符占8个字节),作为索引访问IDT,找到某个门描述符。如果是中断门或陷阱门,那么就取出门描述符中的段选择子和段内偏移量,然后转移到相应的位置去执行。这个过程可以用下图说明:

中断门和陷阱门允许在任务内实施中断处理,转到全局空间执行一些系统级的管理工作。本质上,是任务内的控制转移行为。

2. 通过中断执行任务切换

但是,如果访问IDT时遇到的是任务门,那么就会引发任务切换。即,要中断当前任务的执行并保护现场,然后后切换到另一个任务去执行。

任务门的格式如下图:

  1. 任务门描述符中最重要的字段是段选择子,它应该指向新任务的TSS;
  2. P位指示该门是否有效,当P=0时,不允许通过此门实施任务切换;
  3. DPL是任务门描述符的特权级,但是对于因中断而发起的任务切换不起作用。

注意:任务门描述符可以安装在IDT、GDT和LDT中。

通过中断执行任务切换的过程,可以参考《Intel Architecture Software Developer’s Manual Volume 3:System Programming》的TASK SWITCHING这一节。
我把其中的关键点总结如下(序号不代表发生的先后顺序):
1. 保存当前任务状态到它的TSS(由TR寄存器指向)中;
2. 处理器访问新任务的TSS,从中恢复现场;
3. TR寄存器指向新任务的TSS;
4. 把旧任务的TSS选择子填写到新任务TSS中的“任务链接域”,将新任务EFLAGS寄存器的NT位置1,同时还要把新任务TSS描述符的B位置1,表示该任务状态为忙。(旧任务的TSS的B位不变,仍然为1.)

3. iret指令

当中断发生时,可以执行常规的中断处理过程,也可以执行任务切换。尽管性质不同,但是它们都要使用iret指令返回。前者是返回到同一任务内的断点处;后者是返回到上一层任务。你也许会问:处理器如何区分这两种截然不同的返回类型呢?

如下图所示,32位的EFLAGS寄存器有一个NT位(bit14)—— 嵌套任务标志(Nested Task Flag).

每个任务的TSS中都有一个任务链接域,其内容可以是前一个任务的TSS描述符的选择子。如果当前任务EFLAGS寄存器的NT位是1,则表示当前正在执行的任务嵌套于其他任务内,并且能够通过TSS任务链接域的指针返回到前一个任务。

可以使用iret指令从当前任务返回(切换)到前一个任务,前提是当前任务的NT位必须是1.无论何时处理器遇到iret指令,它都要检查NT位,如果是0,表明是一般中断过程,则按照一般的中断返回处理;如果是1,则表明当前任务之所以能够执行,是因为它打断了别的任务。因此,应当返回到原先被中断的任务继续执行,并且由处理器固件把当前任务EFLAGS的NT位改成0,并把TSS描述符的B位改成0.在保存了当前任务的状态后,接着用被中断的任务的TSS恢复现场。

4. 通过call或者jmp指令发起任务切换

在这两种情况下,call指令或者jmp指令的操作数是任务的TSS描述符选择子或者任务门。例如:

    call 0x10:0x00000000
    jmp 0x10:0x00000000

当处理器执行这两条指令时,首先用选择子索引GDT(对于本例,是GDT),分析得到的描述符类型
1. 对于代码段描述符,就按照普通的段间转移执行;
2. 对于调用门,按照调用门的规则执行;
3. 对于TSS描述符或者任务门(下图中粉色部分),则执行任务切换。此时,指令中给出的32位偏移量被忽略,因为在执行任务切换时,所有处理器的状态都可以从TSS中获得。

使用CALL指令发起的任务切换类似于因中断发起的任务切换。也就是说,由CALL指令发起的任务切换是“嵌套”的。如下图所示:

5. 任务是不可重入的

执行任务切换时,新任务的状态不能为忙。处理器是通过TSS描述符的B位来检测是否重入的。由中断、iret、call和jmp指令发起任务切换时,处理器固件会检测新任务TSS描述符的B位,如果为1,则不允许执行这样的切换。

6. 总结

处理器可以通过以下四种方法实施任务切换:
1. call指令或者jmp指令的操作数是GDT内的某个TSS描述符;
2. call指令或者jmp指令的操作数是GDT或者LDT内某个任务门描述符;
3. 一个异常或者中断发生时,中断号指向IDT内的某个任务门;
4. 在EFLAGS寄存器的NT位置位的情况下,当前任务执行了一个iret指令。

最后,把书上表格15-1再绘制一下,加深印象。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

任务切换的方法——《x86汇编语言:从实模式到保护模式》读书笔记37 的相关文章

  • 从不同线程写入相邻的数组元素?

    是否有任何现代的常见 CPU 从不同线程同时写入数组的相邻元素是不安全的 我对 x86 特别感兴趣 您可能会认为编译器不会做任何明显荒谬的事情来增加内存粒度 即使它在技术上符合标准 我对编写任意大结构的情况感兴趣 而不仅仅是本机类型 Not
  • 汇编语言中数组的冒泡排序

    我需要对一个无组织的数组进行冒泡排序 其中包含从最大到最小的 7 个整数 因此它看起来像 9 6 5 4 3 2 1 我通过编译器运行我的代码 它说 我不明白这段代码有什么问题 code segment assume ds code cs
  • 如何使用 Java 确定 Windows 是 32 位还是 64 位体系结构?

    如何使用 Java 确定 Windows 是 32 位还是 64 位体系结构 我不太相信读取 os arch 系统变量 如果用户在 64 位系统上运行 64 位 JVM 则它可以工作 如果用户在 64 位系统上运行 32 位 JVM 则它不
  • 如何在汇编器中实现相对 JMP (x86)?

    在为 x86 平台构建汇编程序时 我遇到了一些编码问题JMP操作说明 OPCODE INSTRUCTION SIZE EB cb JMP rel8 2 E9 cw JMP rel16 4 because of 0x66 16 bit pre
  • 为什么允许 gcc 从结构推测加载?

    显示 gcc 优化和可能出现故障的用户代码的示例 下面代码片段中的函数 foo 将仅加载结构成员 A 或 B 之一 至少这是未优化代码的意图 typedef struct int A int B Pair int foo const Pai
  • 使用指针作为函数参数时的段前缀

    我有一个汇编 c 问题 我刚刚读到了有关段前缀的内容 例如 ds varX 等 前缀对于逻辑地址的计算很重要 我也读到 默认值是 ds 一旦您使用 ebp 寄存器来计算地址 就会使用 ss 对于代码 cs 是默认值 这一切都是有道理的 现在
  • 链接描述文件未按预期跳过字节

    因此 我有这个汇编文件 我使用 GNU as 进行汇编 并使用链接器脚本与 GNU ld 进行链接 链接描述文件 boot ld INPUT boot o OUTPUT boot out ENTRY boot start SECTIONS
  • 跨 AVX 通道的最佳方式是什么?

    有些问题具有类似的标题 但我的问题涉及其他地方未涵盖的一个非常具体的用例 我有 4 个 128d 寄存器 x0 x1 x2 x3 我想将它们的内容重新组合在 5 个 256d 寄存器 y0 y1 y2 y3 y4 中 以准备其他计算 on
  • 汇编编程语言:程序仅当输入为 ESC 时退出,并在退出前要求确认(y/n),否则循环

    我只是汇编语言编程的初学者 我们的第一个任务是让程序仅在输入为 ESC 时退出 退出之前请求确认 y n 否则循环 我知道 ESC 在 ASCII 代码中具有等效值 但我对插入位置或是否需要添加更多内容感到困惑 请帮我 这是程序 model
  • 为什么当设置为 TLS 选择器时,ES 和 DS 在 64 位内核上最终会归零?

    下面的 32 位程序调用set thread area 2 http linux die net man 2 set thread area在 GDT 中创建一个条目 该条目旨在用于 TLS 通常将结果选择器放入FS or GS并成功使用
  • 使用 gdb 调试反汇编库

    在Linux和Mac OS X中可以使用strapi和next来调试应用程序而无需调试信息 在 Mac OS X 上 gdb 显示在库内部调用的函数 尽管有时会在每个 stepi 指令中推进多个汇编程序指令 在 Linux 上 当我进入动态
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • Intel 64 和 IA-32 上的 MESI 有何意义

    MESI 的要点是保留共享内存系统的概念 然而 对于存储缓冲区 事情就变得复杂了 一旦数据到达 MESI 实现的缓存 下游内存就会保持一致 然而 在此之前 每个核心可能对内存位置 X 中的内容存在分歧 具体取决于每个核心的本地存储缓冲区中的
  • “rep stos”x86 汇编指令序列有什么作用?

    我最近偶然发现了以下汇编指令序列 rep stos dword ptr edi For ecx重复 存储内容eax到哪里edi指向 递增或递减edi 取决于方向标志 每次 4 个字节 通常 这用于memset型操作 通常 该指令简单地写成r
  • AVX-512 指令编码 - {er} 含义

    在 Intel x86 指令集参考中 有许多 AVX 512 指令在指令中具有可选的 er 例如 VADDPD 的一种形式定义为 EVEX NDS 512 66 0F W1 58 r VADDPD zmm1 k1 z zmm2 zmm3 m
  • 设置 IRQ 映射

    我正在遵循一些教程和参考文献来尝试设置我的内核 我在教程中遇到了一些不熟悉的代码 但根本没有解释它 这是我被告知映射的代码16 IRQs 0 15 到 ISR 地点32 47 void irq remap void outportb 0x2
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代
  • 这种没有推送寄存器的交换有多安全?

    我对汇编非常陌生 下面的代码应该通过两个不同的函数交换两个整数 首先使用swap c然后使用swap asm 但我怀疑 我是否需要push 我的意思是保存 汇编代码之前寄存器的每个值和pop稍后 就在返回之前 main 换句话说 如果我返回
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e

随机推荐