摘 要 针对Linux环境下对于异常处理支持的不足,提出了一种解决方案,实现了类似于Windows平台下的结构化异常处理技术,使程序可以更加方便地处理异常,这样提高了代码的健壮性和可维护性。
关键词 Linux;异常;结构化异常处理
在编写程序的过程中,经常会遇到代码因为种种原因发生异常的情况。用户模式下的应用程序还比较好,通常系统不会挂掉,不论是检查现场环境以便调试或是重新运行都比较方便。可驱动程序出现异常麻烦就比较大了,要么丢失错误现场,要么就等着重启动才能再次运行。实在是件让人头痛的事情。
在Windows平台下,这个问题很好解决:SEH(Structure Exception Handling)技术的出现为程序员提供了一个标准解决方案,程序员只需要注册好异常处理函数(由编译器负责,通常不是一个完整的函数,而是和可能发生异常的代码在同一个函数中),当因为当前线程的代码引发异常时,系统会自动调用异常处理函数,完成善后工作。
而Linux下却没有类似的机制,异常处理例程是静态包含在内核代码中,而且必须精确定位出可能产生异常的代码的地址,这无疑会给异常处理带来很大的麻烦。不过反正Linux是开放式的,既然内核自己不提供类似的机制,那就自己来实现它。
要实现SEH,首先需要能够拦截异常,其次是在拦截到异常之后转移到相关的代码,最后,要能在可能产生异常的代码之前注册异常处理例程,并且在代码结束部分取消异常处理例程。
1 异常拦截
要拦截异常,直接修改IDT表,用自己的GP(general-protection)异常处理服务代码替换掉内核的异常处理代码就行,这个功能很容易实现。以下代码实现了拦截异常的功能:
void *_Origin_GP_Entry_;
void _hook_generalfalut()
{
dtr idtr;
desc *pdesc;
asm("sidt %0":"=m"(idtr));
*pdesc=phys_to_virt(idtr->base);
pdesc+=0xd;// point to GP description
_Origin_GP_Entry_=(void*)((pdesc->basehigh<<16)|pdesc->baselow);
pdesc->basehigh=((unsigned long)_ExceptionEntry>>16);
pdesc->baselow=((unsigned long)_ExceptionEntry&0xffff);
}
注意:在程序退出时必须恢复原来的GP异常入口
2 异常处理
在自己的异常服务程序中检测相应的异常处理程序是否已经注册,如果是,则恢复现场,然后跳转到注册的异常处理程序运行;如果没有注册,就把控制权转交给内核的异常处理程序。
|