首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Linux中的被零除异常处理

Linux中的被零除异常处理
EN

Stack Overflow用户
提问于 2012-11-26 19:14:24
回答 1查看 8.9K关注 0票数 7

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

如我所见,除以零异常在trap_init()函数中注册为

代码语言:javascript
运行
复制
set_trap_gate(0, &divide_error);

我想详细了解,在生成INT0和将SIGFPE发送到进程之前,这段时间内发生了什么?

EN

回答 1

Stack Overflow用户

发布于 2014-03-16 04:55:24

陷阱处理程序在arch/x86/kernel/traps.ctrap_init函数中注册

代码语言:javascript
运行
复制
void __init trap_init(void)
..
    set_intr_gate(X86_TRAP_DE, &divide_error);

set_intr_gate将处理程序函数的地址写入idt_table x86/include/asm/desc.h

divide_error函数是如何定义的?作为macro in traps.c

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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中定义为ENTRY(divide_error),在entry_64.S中定义为macro zeroentry1303 zeroentry divide_error do_divide_error)

因此,当用户除以零时(此操作到达OoO中的引退缓冲区),硬件会生成一个陷阱,将%eip设置为divide_error存根,然后设置帧并调用C函数do_divide_error。函数do_divide_error将创建描述错误的siginfo_t结构(signo=SIGFPE,失败指令的addr=地址等),然后它将尝试通知所有向register_die_notifier注册的通知器(实际上它是一个钩子,有时由the in-kernel debugger "kgdb"使用;kprobe的kprobe_exceptions_notify -仅适用于int3或gpf;uprobe的arch_uprobe_exception_notify -同样仅限int3,等等)。

因为通知程序通常不会阻止DIE_TRAP,所以将调用do_trap function。它有一个简短的代码do_trap

代码语言:javascript
运行
复制
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将使用force_sig_infocurrent进程发送一个信号,这将“强制该进程不能忽略的信号”。如果该进程有活动的调试器(我们当前的进程是gdb或strace的ptrace-ed ),那么send_signal会将当前进程的信号SIGFPE从do_trap转换为调试器的SIGTRAP。如果没有调试器-信号SIGFPE应该在保存核心文件时杀死我们的进程,因为这是SIGFPE的默认操作(检查“标准信号”部分中的man 7 signal,在表格中搜索SIGFPE )。

进程不能将SIGFPE设置为忽略它(这里我不确定:1),但它可以定义自己的信号处理程序来处理信号(example of handing SIGFPE another)。此处理程序可能只是打印来自siginfo的%eip,运行backtrace()并退出;或者它甚至可能尝试恢复情况并返回到失败的指令。例如,在qemujavavalgrind等JIT中,或者在javaghc等高级语言中,这可能很有用,这些语言可以将SIGFPE转换为语言异常,并且这些语言中的程序可以处理该异常(例如,来自openjdk is in hotspot/src/os/linux/vm/os_linux.cpp的意大利面)。

在debian中有一个用于siagaction SIGFPEsignal SIGFPE的SIGFPE处理程序列表

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13563688

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档