我很想知道linux中的零异常处理。当执行除零运算时,会生成陷阱,即INT0
被发送到处理器,最终SIGFPE
信号被发送到执行该操作的进程。
如我所见,除以零的异常在trap_init()
函数中注册为
set_trap_gate(0, ÷_error);
我想详细了解在INT0
生成之间和SIGFPE
发送到流程之前发生的事情?
答案 0 :(得分:4)
陷阱处理程序已在arch/x86/kernel/traps.c
的trap_init
函数中注册
void __init trap_init(void)
..
set_intr_gate(X86_TRAP_DE, ÷_error);
set_intr_gate
将处理函数的地址写入idt_table
x86/include/asm/desc.h。
如何定义divide_error函数?作为macro in traps.c
DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
regs->ip)
宏DO_ERROR_INFO
定义为a bit above in the same traps.c:
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 }
(实际上它定义了do_divide_error
函数,该函数由小的asm编码存根"入口点"用准备好的参数调用。宏在entry_32.S
中定义为{{ 3}}和entry_64.S
为ENTRY(divide_error)
:macro zeroentry
)
因此,当用户除以零(并且此操作到达OoO中的报废缓冲区)时,硬件会生成陷阱,将%eip设置为divide_error
存根,它会设置帧并调用C函数{ {1}}。函数do_divide_error
将创建描述错误的do_divide_error
结构(signo = siginfo_t
,addr =失败指令的地址等),然后它将尝试通知所有通知者,注册{ {3}}(实际上它是一个钩子,有时由1303 zeroentry divide_error do_divide_error
使用; kprobe register_die_notifier
- 仅适用于int3或gpf; uprobe' s the in-kernel debugger "kgdb" - 仅限于int3等。)
由于DIE_TRAP通常不会被通知程序阻止,因此将调用kprobe_exceptions_notify。它的代码为SIGFPE
:
do_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 }
会使用arch_uprobe_exception_notify
向do_trap
进程发送一个信号,该信号将强制显示该进程无法忽略的信号" ..如果进程有一个活动的调试器(我们当前的进程是current
- 由gdb或strace编写),那么ptrace
会将信号SIGFPE从send_signal
转换为当前进程到SIGTRAP调试器。如果没有调试器 - 信号SIGFPE应该在保存核心文件时终止我们的进程,因为这是SIGFPE的默认操作(在#34;标准信号"部分中检查do_trap
function,在中搜索SIGFPE表)。
该过程无法设置SIGFPE忽略它(我不确定:force_sig_info
),但它可以定义自己的信号处理程序来处理信号(man 7 signal 1)。这个处理程序可能只是从siginfo打印%eip,运行do_trap
并死掉;或者甚至可以尝试恢复情况并返回失败的指令。这可能在某些JIT中很有用,例如backtrace()
,qemu
或java
;或者在valgrind
或java
等高级语言中,可以将SIGFPE转换为语言异常,这些语言的程序可以处理异常(例如,来自example of handing SIGFPE的spaghetti)。 / p>
通过another或openjdk is in hotspot/src/os/linux/vm/os_linux.cpp
的代码搜索,debian中有一个SIGFPE处理程序列表