为什么中断不是由C代码产生,而是由汇编指令产生?

Why Interrupts not generates by C code but easy generates by assembly instructions?

我正在编写一些内核,并实现 idt 和中断。

我的小内核中的这段 C 代码没有产生任何中断:

int x = 5/0;

int f[4];
f[5] = 8;   

但是这段汇编代码可以产生任何中断:

asm("int [=11=]");

(处理程序正常工作)。

帮助我理解为什么会发生这种情况。

我也试过这个:

    int a = 3;
    int b = 3;  
    int c = a-b;
    int x = a/c;

我在 C 代码中尝试的任何事情都不会为我生成异常。

即使这样也行不通:

int div_by_0(int a, int b){return a/b;}

int x = div_by_0(5, 0);
void fun ( void )
{
    int a = 3;
    int b = 3;
    int c = a-b;
    int x = a/c;
}

Disassembly of section .text:

0000000000000000 <fun>:
   0:   f3 c3                   repz retq 

没有触发被零除的除法。都是死代码。

其中none与int指令有任何关系,这些是完全不同的话题。

如评论中所述,在不使用死代码的情况下对其进行测试。

int fun0 ( int x )
{
    return(5/x);
}
int fun1 ( void )
{
    return(fun0(0));
}

但明白还是达不到预期的效果:

Disassembly of section .text:

0000000000000000 <fun0>:
   0:   b8 05 00 00 00          mov    [=12=]x5,%eax
   5:   99                      cltd   
   6:   f7 ff                   idiv   %edi
   8:   c3                      retq   
   9:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000000010 <fun1>:
  10:   0f 0b                   ud2 

因为 fun1 的优化器可以看到 fun0 函数。您希望在单独的优化域中测试代码。在上面的这种情况下,idiv 将生成除以零。然后它就变成了一个操作系统问题,关于如何处理它以及它是否对您可见。

您看到的问题是因为除以 0 在 C/C++ 中是未定义的行为。编译器已设法在编译时进行足够的优化以实现除以零。编译器可以自由地做任何事情,从停止和着火到使结果为 0。一些编译器会发出 ud2 指令来引发 CPU 异常。结果未定义。

你有几个选择。在汇编中编写除法并从 C/C++ 调用该函数。由于您使用的是 GCC(也适用于 CLANG),您还可以使用内联汇编生成除以零,例如:

#include <stdint.h>  /* or replace uint16_t with unsigned short int */ 

void div_by_0 (void)
{
    asm ("div %b0" :: "a"((uint16_t)0));
    return;
}

这会将 AX 设置为 0,然后将 AX 除以 AL 并得到 DIV 指令。 0/0 未定义,将引发除法异常 (#DE)。此内联汇编应适用于 16、32 和 64 位代码。


在保护模式或长模式下使用 int $#(其中 # 是向量编号)触发异常并不总是与获得 CPU 生成的异常相同。 CPU 生成的 Some exceptions 在需要由中断处理程序清除的 return 地址之后将错误代码压入堆栈。如果您要使用环 0 中的 int [=15=]x0d 导致#GP 异常,中断处理程序可能会出错,因为它来自中断 returns,因为使用 int 生成异常永远不会放置堆栈上的错误代码。这不是 int [=17=] 的问题,因为 #DE 没有由 CPU.

放置在堆栈上的错误代码

原来是优化标志的问题。由于 Makefiles 的一些混乱,-O2 标志起作用了。如果启用 -O0 标志,异常直接从 C 工作。甚至这个简单的代码也会抛出异常:

int x = 5/0;