歡迎您光臨本站 註冊首頁

在Linux中很不正常的異常處理方式

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

  linux下發生異常,晶元會自動產生一個異常中斷。在這異常中斷處理程序中會判斷異常來自用戶程序或者內核,如果是發生在用戶程序,那麼會產生一個異常信號,再根據異常信號的回調函數通知用戶程序發生異常。如果發生在內核裡面,那麼就會搜索內核模塊的異常結構表,找到相應的處理調用地址,修改異常中斷的返回地址為異常處理的地址,中斷返回的時候程序就跳到異常處理程序處理執行了。但具體這兩種處理方法都很糟糕,下面簡要分析一下。

  linux系統把所有進程數據結構都放於內核,這就增加了一些不必要的切換時間。 linux可以通過系統調用,安裝信號的回調函數,這回調函數指針存放在內核的進程數據結構裡面。這點windows處理得比較好,windows把進程數據結構分成了兩部分,一部分敏感數據放於內核的進程數據結構裡面,加以保護,另一部分不敏感數據就放於用戶空間,這樣當訪問那些不加保護的數據時,就不用切換到內核,節約了時間。像windows下異常處理,也是一種回調函數,但因為結構放於用戶空間,安裝的時候就很方便,也節約切換時間。

  上面那一點只是效率問題,但linux內核的異常處理那才是糟糕。先介紹一下linux內核的異常處理結構吧,看明白了你自然就知道糟糕到什麼程度了。要了解這,顯然應該是先從異常中斷入手。下面主要是x86晶元的一些處理,但別的晶元下的也應該差不多。
QUOTE:
文件:entry.S:


ENTRY(general_protection)
pushl $ SYMBOL_NAME(do_general_protection)
jmp error_code


這是異常中斷入口,顯然會執行do_general_protection。文件traps.c:


asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
{
if (regs->eflags & VM_MASK)
goto gp_in_vm86;

/*
虛擬8086下發生的異常否
*/

if (!(regs->xcs & 3))
goto gp_in_kernel;
/*
內核發生的異常否
*/

current->tss.error_code = error_code;
current->tss.trap_no = 13;
force_sig(SIGSEGV, current);
/*
用戶程序發生的異常,產生異常信號,
根據異常信號的句柄回調處理函數
*/
return;

gp_in_vm86:
lock_kernel();
handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
/*
虛擬8086的處理
*/
unlock_kernel();
return;

gp_in_kernel:
{
unsigned long fixup;
fixup = search_exception_table(regs->eip);
/*
根據異常時的eip搜索異常結構鏈
找到處理程序地址
*/
if (fixup) {
regs->eip = fixup;
/*
找到異常處理地址,修改中斷返回地址,中斷返回時跳到異常處理程序處
*/
return;
}
die("general protection fault", regs, error_code);
/*
沒找到異常處理程序地址,顯示內核異常信息后死機
*/

}
}



搜索異常處理程序代碼文件extable.c:


extern const struct exception_table_entry __start___ex_table[];
extern const struct exception_table_entry __stop___ex_table[];

unsigned long search_exception_table(unsigned long addr)
{
unsigned long ret;

#ifndef CONFIG_MODULES
/* There is only the kernel to search. */
ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr);
if (ret) return ret;
#else
/* The kernel is the last "module" -- no need to treat it special. */
struct module *mp;
for (mp = module_list; mp != NULL; mp = mp->next) {
if (mp->ex_table_start == NULL)
continue;
ret = search_one_table(mp->ex_table_start,
mp->ex_table_end - 1, addr);
if (ret) return ret;
}
#endif

return 0;
}

static inline unsigned long
search_one_table(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;

mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid->fixup;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return 0;
}


[火星人 ] 在Linux中很不正常的異常處理方式已經有354次圍觀

http://coctec.com/docs/linux/show-post-189103.html