Exception Linux中的零除异常处理

Exception Linux中的零除异常处理,exception,linux-kernel,signals,Exception,Linux Kernel,Signals,我很想了解linux中的零除异常处理。当执行除零操作时,将生成陷阱,即INT0发送到处理器,并最终SIGFPE信号发送到执行该操作的进程 如我所见,被零除异常在trap\u init()函数中注册为 set_trap_gate(0, &divide_error); 我想详细地知道,在生成INT0和将SIGFPE发送到进程之前,发生了什么事情?陷阱处理程序从 set\u intr\u gate将处理程序函数的地址写入idt\u表 如何定义除法误差函数?作为一个 宏DO\u ERROR\u

我很想了解linux中的零除异常处理。当执行除零操作时,将生成陷阱,即
INT0
发送到处理器,并最终
SIGFPE
信号发送到执行该操作的进程

如我所见,被零除异常在
trap\u init()
函数中注册为

set_trap_gate(0, &divide_error);

我想详细地知道,在生成
INT0
和将
SIGFPE
发送到进程之前,发生了什么事情?

陷阱处理程序从

set\u intr\u gate
将处理程序函数的地址写入
idt\u表

如何定义除法误差函数?作为一个

DO\u ERROR\u INFO
定义如下:

(实际上,它定义了
do_divide_error
函数,该函数由带有准备参数的小型asm编码存根“入口点”调用。宏在
entry_32.S
as和
entry_64.S
as:)中定义)

因此,当用户除以零时(此操作到达OoO中的失效缓冲区),硬件生成陷阱,将%eip设置为
divide\u error
存根,它设置帧并调用C函数
do\u divide\u error
。函数
do\u divide\u error
将创建描述错误的
siginfo\u t
struct(signo=
SIGFPE
,addr=失败指令的地址等),然后它将尝试通知所有已注册的通知程序(实际上它是一个钩子,有时由;kprobe的-仅用于int3或gpf;uprobe的-再次仅用于int3,等等)

由于DIE_陷阱通常不会被通知程序阻止,因此将调用。它有一个短代码
do\u trap

139 static void __kprobes
140 do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
141         long error_code, siginfo_t *info)
142 {
143         struct task_struct *tsk = current;
...
157         tsk->thread.error_code = error_code;
158         tsk->thread.trap_nr = trapnr;
170 
171         if (info)
172                 force_sig_info(signr, info, tsk);
   ...
175 }
do_trap
将向当前进程发送一个信号,该信号将“强制一个进程不能忽略的信号”。。如果进程有一个活动的调试器(我们当前的进程是由gdb或strace设置的
ptrace
),那么
send\u signal
将信号SIGFPE从
do\u trap
转换为SIGTRAP到调试器。如果没有调试器-信号SIGFPE应该在保存核心文件时终止我们的进程,因为这是SIGFPE的默认操作(检查“标准信号”部分,在表中搜索SIGFPE)

进程不能将SIGFPE设置为忽略它(这里我不确定:),但它可以定义自己的信号处理程序来处理信号()。此处理程序可能只是从siginfo打印%eip,运行
backtrace()
并死亡;或者它甚至可以尝试恢复情况并返回失败的指令。例如,在一些JIT中,如
qemu
java
valgrind
,这可能很有用;或者在高级语言中,如
java
ghc
,这可以将SIGFPE转换为语言异常,并且这些语言中的程序可以处理异常(例如,来自的意大利面)

debian中有一个SIGFPE处理程序列表,可通过codesearch查找或查找

此文件的可能副本。
DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
         regs->ip)
193 #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr)         \
194 dotraplinkage void do_##name(struct pt_regs *regs, long error_code)     \
195 {                                                                       \
196         siginfo_t info;                                                 \
197         enum ctx_state prev_state;                                      \
198                                                                         \
199         info.si_signo = signr;                                          \
200         info.si_errno = 0;                                              \
201         info.si_code = sicode;                                          \
202         info.si_addr = (void __user *)siaddr;                           \
203         prev_state = exception_enter();                                 \
204         if (notify_die(DIE_TRAP, str, regs, error_code,                 \
205                         trapnr, signr) == NOTIFY_STOP) {                \
206                 exception_exit(prev_state);                             \
207                 return;                                                 \
208         }                                                               \
209         conditional_sti(regs);                                          \
210         do_trap(trapnr, signr, str, regs, error_code, &info);           \
211         exception_exit(prev_state);                                     \
212 }
139 static void __kprobes
140 do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
141         long error_code, siginfo_t *info)
142 {
143         struct task_struct *tsk = current;
...
157         tsk->thread.error_code = error_code;
158         tsk->thread.trap_nr = trapnr;
170 
171         if (info)
172                 force_sig_info(signr, info, tsk);
   ...
175 }