這里以除0錯(cuò)誤這個(gè)異常來講解異常處理的機(jī)制.
1) 注冊異常處理函數(shù)
在系統(tǒng)初始化的時(shí)候,調(diào)用
trap_init函數(shù)注冊異常處理函數(shù).
這個(gè)函數(shù)里調(diào)用set_trap_gate(0,÷_error);注冊除0錯(cuò)誤的異常由divide_error函數(shù)處理,而這個(gè)錯(cuò)誤的中斷向量號是0.
2) 當(dāng)異常被觸發(fā)時(shí),調(diào)用原先注冊的divide_error函數(shù).
這個(gè)函數(shù)的實(shí)現(xiàn)的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
首先,它將真正的處理函數(shù)do_divide_error壓入棧中,其實(shí),對于每個(gè)異常而言,真正的處理函數(shù)都是名為"do_注冊函數(shù)"的函數(shù).
緊跟著,將一些需要保存的寄存器也壓入棧中.
接著,由于處理函數(shù)的地址已經(jīng)在edi寄存器中了,調(diào)用call *%edi調(diào)用處理函數(shù).
當(dāng)處理完畢之后,調(diào)用函數(shù)ret_from_exception從異常處理中返回.
上面是大致的流程,下面詳細(xì)看看do_divide_error函數(shù)做了什么.
這個(gè)函數(shù)的實(shí)現(xiàn)在文件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)
可以看到,最終這個(gè)函數(shù)會走到do_trap函數(shù)中,接著看這個(gè)函數(shù)中的代碼片段:
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;
}
首先得到當(dāng)前進(jìn)程的指針,在進(jìn)程結(jié)構(gòu)體的thread結(jié)構(gòu)體中保存error_code和trapnr,也就是錯(cuò)誤號和中斷向量.
接著調(diào)用force_sig_info函數(shù),可以跟進(jìn)這個(gè)函數(shù),其實(shí)最終要做的就是將該異常以信號量的形式加入到當(dāng)前進(jìn)程的信號集合中,也就是給當(dāng)前進(jìn)程發(fā)送信號,告訴進(jìn)程有異常被觸發(fā)了,需要處理.以除0錯(cuò)誤來看,這個(gè)信號量是SIGFPE.