科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Linux操作系统下糟糕的异常处理方式 (1)

Linux操作系统下糟糕的异常处理方式 (1)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

linux下发生异常,芯片会自动产生一个异常中断。在这异常中断处理程序中会判断异常来自用户程序或者内核,如果是发生在用户程序,那么会产生一个异常信号,再根据异常信号的回调函数通知用户程序发生异常。

作者:赛迪网技术社区 来源:赛迪网技术社区 2007年10月23日

关键字: 处理 异常 操作系统 Linux

  • 评论
  • 分享微博
  • 分享邮件
 

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;
}
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章