当内核走到panic时表明其已无法继续运行下去,因此需要执行一些系统挂死前的准备工作,其主要包含以下部分:
(1)在smp系统中,一个cpu正在处理panic时,可能另一个cpu也会触发panic。而该流程主要用于一些错误信息收集、内存转储等工作,并不需要也不支持并发操作。因此对于后续触发的cpu不需要执行该流程
(2)若正在使用kgdb对内核进行调试,则显然希望调试器能继续执行调试工作。故此时不会真正将系统挂死,而是将控制权转交给调试器
(3)若内核配置了kdump等内存转储功能,则在panic时将启动转储相关的流程
(4)在smp系统挂死之前,需要停止所有其它cpu的运行,以使系统真正地停下来
(5)最后,打印相关的系统信息后,使系统重启或进入死循环
其相应的代码实现如下:
void panic(const char *fmt, ...)
{
…
this_cpu = raw_smp_processor_id();
old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu);
if (old_cpu != PANIC_CPU_INVALID && old_cpu != this_cpu) (1)
panic_smp_self_stop();
…
pr_emerg("Kernel panic - not syncing: %s\n", buf);
…
kgdb_panic(buf); (2)
if (!_crash_kexec_post_notifiers) {
printk_safe_flush_on_panic();
__crash_kexec(NULL); (3)
smp_send_stop(); (4)
} else {
crash_smp_send_stop(); (5)
}
atomic_notifier_call_chain(&panic_notifier_list, 0, buf); (6)
printk_safe_flush_on_panic();
kmsg_dump(KMSG_DUMP_PANIC); (7)
if (_crash_kexec_post_notifiers)
__crash_kexec(NULL); (8)
…
panic_print_sys_info(); (9)
if (!panic_blink)
panic_blink = no_blink;
if (panic_timeout > 0) {
pr_emerg("Rebooting in %d seconds..\n", 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); (10)
}
}
if (panic_timeout != 0) {
if (panic_reboot_mode != REBOOT_UNDEFINED)
reboot_mode = panic_reboot_mode;
emergency_restart(); (11)
}
…
pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf);
suppress_printk = 1;
local_irq_enable();
for (i = 0; ; i += PANIC_TIMER_STEP) {
touch_softlockup_watchdog();
if (i >= i_next) {
i += panic_blink(state ^= 1);
i_next = i + 3600 / PANIC_BLINK_SPD;
}
mdelay(PANIC_TIMER_STEP); (12)
}
}
(1)若先前已经有cpu正在处理panic流程,则本cpu不再重复处理,只需将当前cpu停止
(2)打印panic原因信息
(3)若panic流程会执行内存转储,则所有系统相关信息都会被保存到转储文件中,因此就不需要调用后面的通知链,因此可直接调用转储操作。但是转储操作也不是100%保险,因此若不是对其绝对信任,则会设置_crash_kexec_post_notifiers
,它会先执行通知链调用和log dump相关流程,再调用内核转储操作。
__crash_kexec
函数会根据当前是否设置了转储内核确定是否实际执行转储操作,若执行转储则会通过kexec将系统切换到新的kdump内核,并且不再会返回。若不执行转储则继续执行后续流程
(4 - 5)停止当前cpu之外的其它cpu运行
(6)调用关心panic事件相关模块向其注册的通知
(7)dump内核log buffer中的log信息
(8)若设置了_crash_kexec_post_notifiers
,则根据是否设置了kexec内核,确定是否执行内存转储操作
(9)若不执行内存转储,则打印系统相关信息
(10)若设置了panic_timeout
超时值,则执行超时等待操作
(11)若设置了panic_timeout
超时值,在超时等待完成后重启系统
(12)若未设置panic_timeout
超时值,则将系统设置为死循环状态,使其挂死