Nik*_*ike 5 signals exception linux-kernel
我很好奇理解linux中的零除异常处理。当执行零除运算时,将生成陷阱,即将陷阱INT0
发送到处理器,并最终将SIGFPE
信号发送到执行该操作的进程。
如我所见,除零异常在trap_init()
函数中注册为
set_trap_gate(0, ÷_error);
Run Code Online (Sandbox Code Playgroud)
我想详细了解一下,在INT0
生成之前和SIGFPE
发送到流程之前发生了什么?
陷阱处理程序trap_init
已从arch / x86 / kernel / traps.c注册在函数中
void __init trap_init(void)
..
set_intr_gate(X86_TRAP_DE, ÷_error);
Run Code Online (Sandbox Code Playgroud)
set_intr_gate
将处理程序函数的地址写入idt_table
x86 / include / asm / desc.h中。
如何定义Divor_Error函数?作为宏traps.c
DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
regs->ip)
Run Code Online (Sandbox Code Playgroud)
和宏DO_ERROR_INFO
定义比特上述相同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 }
Run Code Online (Sandbox Code Playgroud)
(实际上它定义了do_divide_error
这是由小ASM编码存根“入口点”与准备参数调用功能宏中定义。entry_32.S
如ENTRY(divide_error)
与entry_64.S
作为宏zeroentry
:1303 zeroentry divide_error do_divide_error
)
因此,当用户除以零(并且此操作到达OoO中的退出缓冲区)时,硬件会生成陷阱,将%eip设置为divide_error
stub,然后设置框架并调用C函数do_divide_error
。该函数do_divide_error
将创建siginfo_t
描述错误的结构(signo = SIGFPE
,addr =失败指令的地址等),然后它将尝试通知所有已注册的通知程序register_die_notifier
(实际上是一个钩子,有时被内核调试器使用” kgdb“; kprobe的kprobe_exceptions_notify-仅用于int3或gpf; uprobe的arch_uprobe_exception_notify
-再次仅int3等)。
由于DIE_TRAP通常不会被通知程序阻止,因此将调用该do_trap
函数。它的简短代码为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 }
Run Code Online (Sandbox Code Playgroud)
do_trap
会使用发送信号到current
进程force_sig_info
,该信号将“强制发出该进程不能忽略的信号”。如果该进程有一个活动的调试器(我们的当前进程ptrace
由gdb或strace -ed),send_signal
则将翻译从当前信号SIGFPE do_trap
到SIGTRAP到调试器的信号。如果没有调试器,则信号SIGFPE应该在保存核心文件的同时终止我们的进程,因为这是SIGFPE的默认操作(请在“标准信号”部分中检查man 7信号,在表中搜索SIGFPE)。
该过程无法将SIGFPE设置为忽略它(我不确定此处:1),但是它可以定义自己的信号处理程序来处理信号(将SIGFPE交给 另一个示例)。该处理程序可能只是从siginfo打印%eip,然后运行backtrace()
并死亡;甚至可能尝试恢复情况并返回失败的指令。在一些即时编译器喜欢,这可能是有用的,例如qemu
,java
,或valgrind
; 或高级语言(如java
或)中ghc
,这些语言可以将SIGFPE转换为语言异常,而使用这些语言的程序可以处理该异常(例如,来自openjdk的hotspot/src/os/linux/vm/os_linux.cpp
意大利面条在中)。
通过代码搜索在debian中有一个SIGFPE处理程序列表,用于关联SIGFPE或信号SIGFPE
归档时间: |
|
查看次数: |
6328 次 |
最近记录: |