linux内核V2.6.11学习笔记(5)--异常处理

linux内核V2.6.11学习笔记(5)--异常处理

这里以除0错误这个异常来讲解异常处理的机制.

1) 注册异常处理函数
在系统初始化的时候,调用 trap_init函数注册异常处理函数.
这个函数里调用set_trap_gate(0,&divide_error);注册除0错误的异常由divide_error函数处理,而这个错误的中断向量号是0.

2) 当异常被触发时,调用原先注册的divide_error函数.
这个函数的实现的Entry.S文件中:
ENTRY(divide_error)
    pushl $
0             # no error code
    pushl $do_divide_error
    ALIGN
error_code:
    pushl 
% ds
    pushl 
% eax
    xorl 
% eax,  % eax
    pushl 
% ebp
    pushl 
% edi
    pushl 
% esi
    pushl 
% edx
    decl 
% eax            # eax  =   - 1
    pushl 
% ecx
    pushl 
% ebx
    cld
    movl 
% es,  % ecx
    movl ES(
% esp),  % edi        #  get  the function address
    movl ORIG_EAX(
% esp),  % edx    #  get  the error code
    movl 
% eax, ORIG_EAX( % esp)
    movl 
% ecx, ES( % esp)
    movl $(__USER_DS), 
% ecx
    movl 
% ecx,  % ds
    movl 
% ecx,  % es
    movl 
% esp, % eax            # pt_regs pointer
    call 
*% edi
    jmp ret_from_exception
首先,它将真正的处理函数do_divide_error压入栈中,其实,对于每个异常而言,真正的处理函数都是名为"do_注册函数"的函数.
紧跟着,将一些需要保存的寄存器也压入栈中.
接着,由于处理函数的地址已经在edi寄存器中了,调用call *%edi调用处理函数.
当处理完毕之后,调用函数ret_from_exception从异常处理中返回.

上面是大致的流程,下面详细看看do_divide_error函数做了什么.
这个函数的实现在文件trap.c中:
#define  DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
fastcall 
void  do_##name( struct  pt_regs  *  regs,  long  error_code) \
{ \
    siginfo_t info; \
    info.si_signo 
=  signr; \
    info.si_errno 
=   0 ; \
    info.si_code 
=  sicode; \
    info.si_addr 
=  ( void  __user  * )siaddr; \
    
if  (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
                        
==  NOTIFY_STOP) \
        
return ; \
    do_trap(trapnr, signr, str, 
1 , regs, error_code,  & info); \
}

DO_VM86_ERROR_INFO( 
0 , SIGFPE,   " divide error " , divide_error, FPE_INTDIV, regs -> eip)
可以看到,最终这个函数会走到do_trap函数中,接着看这个函数中的代码片段:
    trap_signal: {
        
struct  task_struct  * tsk  =  current;
        tsk
-> thread.error_code  =  error_code;
        tsk
-> thread.trap_no  =  trapnr;
        
if  (info)
            force_sig_info(signr, info, tsk);
        
else
            force_sig(signr, tsk);
        
return ;
    }
首先得到当前进程的指针,在进程结构体的thread结构体中保存error_code和trapnr,也就是错误号和中断向量.
接着调用force_sig_info函数,可以跟进这个函数,其实最终要做的就是将该异常以信号量的形式加入到当前进程的信号集合中,也就是给当前进程发送信号,告诉进程有异常被触发了,需要处理.以除0错误来看,这个信号量是SIGFPE.



你可能感兴趣的:(linux内核V2.6.11学习笔记(5)--异常处理)