英特尔x86-中断服务例程职责 [英] Intel x86 - Interrupt Service Routine responsibility

查看:20
本文介绍了英特尔x86-中断服务例程职责的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我没有这个词的真正意义上的问题,而是我将尝试澄清一个内容问题。假设我们有一个微内核(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编写代码。您正在尝试处理被零除错误生成的异常,就好像它是由硬件中断生成的。但是,这不是英特尔x86处理器处理此类异常的方式。

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

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

故障

如果异常的性质允许以某种方式纠正该异常,则处理器将该异常作为错误处理。因此,堆栈上压入的返回地址指向生成异常的指令,因此故障处理程序知道导致故障的确切指令,并使修复问题后恢复执行出错指令成为可能。页面错误(#pf)异常就是一个很好的例子。它可用于实现虚拟内存,方法是让故障处理程序为出错指令试图访问的地址提供有效的虚拟映射。有了有效的页面映射,指令就可以恢复并执行,而不会产生另一个页面错误。

陷阱

中断和某些类型的异常(所有软件异常)都被作为陷阱处理。陷阱并不意味着指令执行中出现错误。硬件中断发生在指令执行之间,而软件中断和某些软件异常有效地模拟了这种行为。通过压入正常执行的下一条指令的地址来处理陷阱。这允许陷阱处理程序继续正常执行中断的代码。

中止

严重且无法恢复的错误将作为中止处理。只有两个异常会产生中止,即机器检查(#mc)异常和双重故障(#df)。机器检查指令是检测到处理器本身的硬件故障的结果,这是无法修复的,也无法可靠地恢复正常执行。当在处理中断或异常期间发生异常时,就会发生双重故障异常。这使得CPU处于不一致的状态,在调用ISR的所有必要步骤中的某个位置,无法恢复。推送到堆栈上的返回值可能与导致中止的原因有关,也可能与此无关。

通常如何处理Divide错误异常

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

请注意,虽然您可以通过"递增EIP"来处理Divide错误异常,但这比您想象的要难,并且不会产生非常有用的结果。您不能仅将一个或一些其他常量值添加到EIP,您需要跳过整个指令,其长度可能在2到15个字节之间。AAM、DIV和IDIV这三条指令可能会导致此异常,这些指令可以使用各种前缀和操作数字节进行编码。你需要对指令进行解码才能算出它有多长。执行此增量的结果将如同该指令从未执行过一样。出错指令不会计算有意义的值,您也不会知道程序运行不正确的原因。

阅读文档

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

这篇关于英特尔x86-中断服务例程职责的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆