assembly - 英特尔x86-中断服务程序责任

标签 assembly x86 intel interrupt osdev

我并没有真正意义上的问题,但是我将尽力澄清一个内容问题。假设我们有一个微内核(PC Intel x86; 32位保护模式),具有针对每个CPU异常的有效的中断描述符表(IDT)和中断服务例程(ISR)。如果发生Division by Zero异常,则成功调用了ISR。

global ir0
extern isr_handler

isr0:

    cli
    push 0x00   ; Dummy error code
    push %1     ; Interrupt number

    jmp isr_exc_handler

isr_exc_handler:

; Save the current processor state

    pusha

    mov ax, ds
    push eax

    mov ax, 0x10 ; Load kernel data segment descriptor
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ; Push current stack pointer

    mov eax, esp
    push eax

    call isr_handler ; Additional C/C++ handler function

    pop eax     ; Remove pushed stack pointer

    pop ebx     ; Restore original data segment descriptor
    mov ds, bx
    mov es, bx
    mov fs, bx
    mov gs, bx

    popa

    add esp, 0x08 ; Clean up pushed error code and ISR number
    sti

    iret


问题是该中断一次又一次地引发。结果,一次又一次地调用ISR。通过反复试验,我发现引发异常的那一行,
int x = 5 / 0是循环执行的,因此指令指针(EIP)不递增。

当我增加手动推入堆栈的IP的值时,会发生预期的行为。然后,CPU在恶意代码行之后执行下一条指令。当然,ISR被召集一次之后。

对于我的实际问题:ISR是否有必要增加IP?还是这是“ CPU /硬件”的责任?继续前进的正确行为是什么?

最佳答案

您有责任知道处理器将如何以及为何调用中断服务例程,并相应地为ISR编写代码。您正在尝试将零除错误生成的异常视为由硬件中断生成。但是,这不是Intel x86处理器处理此类异常的方式。

x86处理器如何处理中断和异常

存在几种不同类型的事件,这些事件将导致处理器调用中断向量表中给出的中断服务程序。这些统称为中断和异常,处理器可以通过三种不同方式来处理中断或异常,作为故障,陷阱或中止。您的除法指令生成一个除法错误(#DE)异常,将其作为故障处理。硬件和软件中断被视为陷阱,而其他种类的异常则作为这三种方式之一来处理,具体取决于异常的来源。

缺点

如果异常的性质允许以某种方式对其进行纠正,则处理器会将异常作为错误处理。因此,压入堆栈的返回地址指向生成异常的指令,因此故障处理程序可以知道是什么确切的指令导致了故障,并有可能在解决问题后恢复执行该故障的指令。 Page Fault(#PF)异常是一个很好的例子。通过使故障处理程序为故障指令尝试访问的地址提供有效的虚拟映射,可以用来实现虚拟内存。有了有效的页面映射,就可以继续执行该指令,而不会产生另一个页面错误。

陷阱

中断和某些类型的异常(全部都是软件异常)被视为陷阱。陷阱并不意味着指令执行中的错误。硬件中断发生在指令执行之间,软件中断和某些软件异常有效地模仿了这种行为。陷阱是通过按入将正常执行的下一条指令的地址来处理的。这使陷阱处理程序可以恢复被中断代码的正常执行。

中止

严重且不可恢复的错误被视为中止。只有两个异常会产生异常,机器检查(#MC)异常和双重故障(#DF)。机器检查指令是检测到处理器本身发生硬件故障的结果,无法修复,不能可靠地恢复正常执行。当在中断或异常的处理过程中发生异常时,会发生双重故障异常。这使CPU处于不一致状态,处于调用ISR的所有许多必要步骤的中间,而这些步骤无法恢复。压入堆栈的返回值与中止的原因无关。

通常如何处理除法错误异常

通常,大多数操作系统通过将除错异常传递给正在执行的进程中的处理程序来进行处理,或者通过终止进程而失败(表明该进程已崩溃)来处理该错误。例如,大多数Unix系统将SIGFPE信号发送给进程,而Windows使用其结构化异常处理机制执行类似的操作。这样一来,流程的编程语言运行时就可以设置自己的处理程序,以实现所使用的编程语言所必需的任何行为。由于零除导致C和C ++中的行为不确定,因此崩溃是可以接受的行为,因此这些语言通常不会安装零除处理程序。

请注意,虽然可以通过“递增EIP”来处理除法错误异常,但这比您想象的要难,并且不会产生非常有用的结果。您不能只向EIP添加一个或其他常量值,还需要跳过整个指令,该指令的长度可能在2到15个字节之间。 AAM,DIV和IDIV是三种可能导致此异常的指令,它们可以使用各种前缀和操作数字节进行编码。您需要对指令进行解码,以找出它有多长。执行此递增的结果将好像从未执行过该指令。错误的指令将无法计算出有意义的值,并且您将无法指示程序为什么无法正常运行。

阅读文档

如果您要编写自己的操作系统,则需要提供《英特尔软件开发人员手册》,以便您经常查阅。特别是,您需要阅读和学习第3卷:系统编程指南中的几乎所有内容,但不包括“虚拟机扩展”章节以及之后的所有内容。有关中断和异常的详细信息,您需要了解的所有内容,以及您需要了解的许多其他信息。

关于assembly - 英特尔x86-中断服务程序责任,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59218028/

相关文章:

linux - 无法使用 hikey_defconfig 编译 u-boot

c - 在 Assembly Works 中如何调用带有大量参数的函数

assembly - 如何为 mov eax,moffs32 生成 a1 操作码

intel - vaainfo 中的 vaapi 权限问题

performance - 至强 CPU (E5-2603) 后向内存预取

c - 如何将 Intel Assembly C 转换为 AT&T C++

go - 使用 Go 从 RDPMC 获取值(value)

c - 使用 GCC 中的内联汇编从 32 位 IMUL 返回 64 位结果

linux - 使用汇编在 Linux 终端中清除屏幕?

android - Cardboard 库无法在基于英特尔的设备上加载