黑客风云——风云网络
设为首页 加入收藏 我要投稿 网站地图
您现在的位置: 黑客风云 >> 黑客文章 >> 操作系统 >> LINIX UNIX >> 文章正文
[推荐]Linux操作系统下糟糕的异常处理方式
        ★★★
Linux操作系统下糟糕的异常处理方式
文章整理发布:黑客风云 文章来源:www.05112.com 更新时间:2006-11-22
 linux下发生异常,芯片会自动产生一个异常中断。在这异常中断处理程序中会判断异常来自用户程序或者内核,如果是发生在用户程序,那么会产生一个异常信号,再根据异常信号的回调函数通知用户程序发生异常。如果发生在内核里面,那么就会搜索内核模块的异常结构表,找到相应的处理调用地址,修改异常中断的返回地址为异常处理的地址,中断返回的时候程序就跳到异常处理程序处理执行了。但具体这两种处理方法都很糟糕,下面简要分析一下。

  linux系统把所有进程数据结构都放于内核,这就增加了一些不必要的切换时间。 linux可以通过系统调用,安装信号的回调函数,这回调函数指针存放在内核的进程数据结构里面。这点windows处理得比较好,windows把进程数据结构分成了两部分,一部分敏感数据放于内核的进程数据结构里面,加以保护,另一部分不敏感数据就放于用户空间,这样当访问那些不加保护的数据时,就不用切换到内核,节约了时间。像windows下异常处理,也是一种回调函数,但因为结构放于用户空间,安装的时候就很方便,也节约切换时间。

  上面那一点只是效率问题,但linux内核的异常处理那才是糟糕。先介绍一下linux内核的异常处理结构吧,看明白了你自然就知道糟糕到什么程度了。要了解这,显然应该是先从异常中断入手。下面主要是x86芯片的一些处理,但别的芯片下的也应该差不多。

  文件: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; 
}

  看看上面搜索异常处理程序的算法就知道了,有个异常模块表,保存会发生异常时候的eip和异常处理程序指针,发生异常的时候就根据异常时候的eip搜索表里面的eip,发现相等就找到了异常处理指针。这是什么意思呢,就是说你编写的内核程序必须精确的知道哪条指令可能会发生异常,要求真够高的。想想windows下的异常编程是多么轻松?程序员只需要知道哪一段程序可能出现异常,就只需要一个括号一个异常语句保护这段程序就是了。

[1] [2] 下一页  

文章录入:sygbox    责任编辑:sygbox 
【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
VIP 专 区
Copyright @2006 黑客风云 ●业务联系:QQ 联系怪人 联系奇人 Email:给怪人发邮件 给奇人发邮件
ICP备案:冀06009886