异常处理函数,在底层【汇编代码】做一些处理后,最后调用C接口函数。即使是C函数,内核异常的处理函数还是CPU架构相关的,就是函数在arch下的不同目录下实现。
未定义指令的处理:
当发生指令异常时,汇编代码的处理是[以64位ARMV8架构为例]:
对应的汇编代码在文件kernel/arch/arm64/kernel/entry.S中,
el1_undef:
/*
* Undefined instruction
*/
enable_dbg
mov x0,sp
b do_undefinstr
do_undefinstr的实现在具体CPU架构的文件kernel/arch/arm64/kernel/traps.c中,不同异常处理一般都调用函数arm64_notify_die,arm64_notify_die只是做了简单的处理,最后调用die
do_undefinstr
->arm64_notify_die("Oops- undefined instruction", regs, &info, 0);
->die
die主要由三个子函数oop_begin,__die和oop_end组成,内核异常后的log主要有这三个函数输出。
voiddie(const char *str, struct pt_regs *regs, int err)
{
unsignedlong flags = oops_begin();
ret= __die(str, err, thread, regs);
oops_end(flags,regs, ret);
}
oops_begin()没有打印信息,log主要由函数__die输出,如backtrace等
staticint __die(const char *str, int err, struct thread_info *thread,
struct pt_regs *regs)
{
pr_emerg("Internalerror: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
str, err, ++die_counter);
print_modules();
__show_regs(regs);
pr_emerg("Process%.*s (pid: %d, stack limit = 0x%p)\n",
TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
if(!user_mode(regs) || in_interrupt()) {
dump_backtrace(regs,tsk);
dump_instr(KERN_EMERG,regs);
}
}
如以下打印信息,都有__die函数输出;
[ 140.325767] Internal error: Oops: 96000045 [#1] PREEMPT SMP
[ 140.325800] Modules linked in: wlan(O)
[ 140.325870] CPU: 0 PID: 5011 Comm: sh Tainted: G W O 3.18.20-perf-ga0cfe42 #1
[ 140.325895] Hardware name: Qualcomm TechnologiesPC i, Inc. MSM 8996v3 + PMI8994 MTP (DT)
[ 140.325925] task: ffffffc004e8a580 ti: ffffffc05809c000 task.ti:ffffffc05809c000
[ 140.325983] PC is at sysrq_handle_crash+0x14/0x1c
[ 140.326010] LR is at __handle_sysrq+0x9c/0x150
[ 140.332644] Process sh (pid: 5011, stack limit = 0xffffffc05809c058)
[ 140.332669] Call trace:
[ 140.332712] [<ffffffc000624ecc>] sysrq_handle_crash+0x14/0x1c
[ 140.332741] [<ffffffc000625ac8>] write_sysrq_trigger+0x48/0x60
[ 140.332784] [<ffffffc0003704f4>] proc_reg_write+0x64/0x84
[ 140.332817] [<ffffffc000320938>] vfs_write+0xb8/0x194
[ 140.332842] [<ffffffc000320fb4>] SyS_write+0x44/0x84
[ 140.332872] Code: 52800020 b909f420 d5033e9f d2800001 (39000020)
下面看die的最后一个函数:oop_end
staticvoid oops_end(unsigned long flags, struct pt_regs *regs, int notify)
{
oop_exit()
if(in_interrupt())
panic("Fatalexception in interrupt");
if(panic_on_oops)
panic("Fatalexception");
}
函数oop_exit和panic在CPU无关的文件kernel/kernel/panic.c中
oops_exit会输出信息---[end trace da227214a82491b9 ]---
oops_exit(void)->print_oops_end_marker()
->pr_warn("---[end trace %016llx ]---\n", (unsigned long long)oops_id);
内核异常后,整机会重启,重启就是在函数panic控制的,方法就是进入死循环,利用硬件看门狗超时使系统重启。
voidpanic(const char *fmt, ...)
{
pr_emerg("Kernelpanic - not syncing: %s\n", buf);
if(!test_taint(TAINT_DIE) && oops_in_progress <= 1)
dump_stack();
/*
* Run any panic handlers, including those that might need to
* add information to the kmsg dump output.
*/
atomic_notifier_call_chain(&panic_notifier_list,0, buf);
if(panic_timeout > 0) {
/*
* Delay timeout seconds before rebooting the machine.
* We can't use the "normal" timers since we just panicked.
*/
pr_emerg("Rebootingin %d seconds..", panic_timeout);
for(i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {
touch_nmi_watchdog();
if(i >= i_next) {
i+= panic_blink(state ^= 1);
i_next= i + 3600 / PANIC_BLINK_SPD;
}
mdelay(PANIC_TIMER_STEP);
}
}
}
数据访问异常
dataabort异常并非都会导致系统重启,虚拟内存的缺页机制也是通过数据访问异常实现的。
ENTRY(v6_early_abort)
mrc p15,0, r1, c5, c0, 0 @ get FSR
mrc p15,0, r0, c6, c0, 0 @ get FAR
b do_DataAbort
arch/arm/mm/fault.c:550:do_DataAbort(unsignedlong addr, unsigned int fsr, struct pt_regs *regs)
asmlinkagevoid __exception
do_DataAbort(unsignedlong addr, unsigned int fsr, struct pt_regs *regs)
{
conststruct fsr_info *inf = fsr_info + fsr_fs(fsr);
structsiginfo info;
if(!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
return;
trace_unhandled_abort(regs,addr, fsr);
printk(KERN_ALERT"Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name,fsr, addr);
info.si_signo= inf->sig;
info.si_errno= 0;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
arm_notify_die("",regs, &info, fsr, 0);
}
do_DataAbort的异常处理并没有调用arm_notify_die("",regs, &info, fsr, 0);
而是inf->fn(addr,fsr & ~FSR_LNX_PF, regs): c0115920 t do_page_fault
do_page_fault->
__do_kernel_fault(mm,addr, fsr, regs);
/*
*Oops. The kernel tried to access some page that wasn't present.
*/
staticvoid
__do_kernel_fault(structmm_struct *mm, unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
/*
* Are we prepared to handle this kernel fault?
*/
if(fixup_exception(regs))
return;
/*
* No handler, we'll have to terminate things with extreme prejudice.
*/
bust_spinlocks(1);
printk(KERN_ALERT
"Unableto handle kernel %s at virtual address %08lx\n",
(addr< PAGE_SIZE) ? "NULL pointer dereference" :
"pagingrequest", addr);
show_pte(mm,addr);
die("Oops",regs, fsr);
}
直接重启
直接重启,没有进入异常处理流程,没有backtrace等信息的保存。利用pstore机制,保存系统最后时间运行的kernellog 和logcat.