2012-11-26 75 views
3

我很好奇在linux中理解除以零異常處理。當執行除零操作時,產生陷阱,即,將INT0發送到處理器,並最終將信號發送到執行該操作的過程。在Linux中除以零異常處理

依我之見,零異常的鴻溝是註冊於trap_init()功能

set_trap_gate(0, &divide_error); 

我想詳細的瞭解,什麼都INT0之間發生在產生和SIGFPE之前被送到處理?

+0

可能重複[爲什麼Linux內核使用陷阱門來處理鴻溝\ _error例外?](http://stackoverflow.com/問題/ 8530794/why-linux-kernel-use-trap-gate-to-handle-divide-error-exception)檢查出這個** [answer](http://stackoverflow.com/a/15501983/319204)* *。 – TheCodeArtist

回答

3

陷阱處理程序在trap_init函數註冊從arch/x86/kernel/traps.c

void __init trap_init(void) 
.. 
    set_intr_gate(X86_TRAP_DE, &divide_error); 

set_intr_gate寫處理函數的地址複製到idt_tablex86/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.SENTRY(divide_error)entry_64.Smacro 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短代碼:

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會發出一個信號發送到current過程與force_sig_info,這將「力的信號,這一進程不能忽視」。如果有一個積極的調試器(我們當前的進程是由gdb或strace執行的ptrace),那麼send_signal會將信號SIGFPE轉換爲當前進程從do_trap到SIGTRAP到調試器。如果沒有調試器 - 信號SIGFPE應該在保存核心文件時終止我們的進程,因爲這是SIGFPE的默認操作(在「標準信號」部分查詢man 7 signal,在表格中搜索SIGFPE)。

該進程無法設置SIGFPE忽略它(我不確定這裏:1),但它可以定義自己的信號處理程序來處理信號(example of handing SIGFPEanother)。這個處理程序可能只是打印siginfo的%eip,運行backtrace()並死亡;或者甚至可能試圖恢復情況並返回失敗的指令。例如,在某些JIT中可能會有用,如qemu,javavalgrind;或者使用高級語言如javaghc,這可能會將SIGFPE轉換爲語言異常,並且這些語言的程序可以處理該異常(例如,來自openjdk is in hotspot/src/os/linux/vm/os_linux.cpp的意大利麪條)。

沒有爲siagaction SIGFPE或在通過於codesearch Debian的SIGFPE處理程序的列表signal SIGFPE