自动化任务执行过程中出现了几次晚上无故自动重启的情况,今天正好看到相关的日志输出,才发现是触发了内核panic导致重启,目前已进行相关配置,正在重新验证。
问题过程
自动化任务执行中有一个过程是需要将系统设置为只读,然后利用dd命令将磁盘分区备份出来,其中设置文件系统只读使用的是:
echo u > /proc/sysrq-trigger
具体这个过程,后面再详细说明,执行该命令可以将文件系统重新挂载为只读,避免dd命令过程中系统操作磁盘内容。
经过一段时间运行,会偶尔出现dd命令执行过程中无故重启,今天排查重启前后日志时,正好发现一个问题,就是内核日志提示:
[ 960.650132] INFO: task kworker/u8:2:1022 blocked for more than 120 seconds.
[ 960.657894] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 960.668277] BUG: using smp_processor_id() in preemptible [00000000] code: khungtaskd/662
[ 960.698381] Kernel panic - not syncing: hung_task: blocked tasks
于是查找kernel panic相关话题,发现了问题,是执行流程中的只读配置触发了内核重启。
hung task 检测机制
我们知道进程等待IO时,经常处于D状态,即TASK_UNINTERRUPTIBLE
状态,处于这种状态的进程不处理信号,所以kill不掉,如果进程长期处于D状态,那么肯定不正常,原因可能有如下两种:
- IO路径上的硬件出问题了,比如磁盘损坏;
- 内核自己出问题了。
这种问题一般不好定位,而且一旦出现就通常不可恢复,kill不掉,只能重启恢复了。
内核针对这种问题,开发了一种hung task的检测机制,基本原理就是定时检测系统中处于D状态的晋城,如果其处于D状态的时间超过了制定时间(一般默认120s,可以配置),则打印相关堆栈信息,也可以通过proc参数配置使其之间直接panic。
内核panic是什么
panic函数主要是内核出现异常的时候输出异常信息、异常栈信息、挂起系统,代码如下:
/**
* panic - halt the system
* @fmt: The text string to print
*
* Display a message, then perform cleanups.
*
* This function never returns.
*/
void panic(const char *fmt, ...)
{
static DEFINE_SPINLOCK(panic_lock);
static char buf[1024];
va_list args;
long i, i_next = 0;
int state = 0;
/*
* It's possible to come here directly from a panic-assertion and
* not have preempt disabled. Some functions called from here want
* preempt to be disabled. No point enabling it later though...
*
* Only one CPU is allowed to execute the panic code from here. For
* multiple parallel invocations of panic, all other CPUs either
* stop themself or will wait until they are stopped by the 1st CPU
* with smp_send_stop().
*/
if (!spin_trylock(&panic_lock))
panic_smp_self_stop(); /*同一时间只有一个核能执行panic代码,获取到锁的cpu先把自己stop掉,其他核要么自己stop自己 要么等待拿到锁的核执行smp_send_stop*/
console_verbose();/* 提高打印等级 */
bust_spinlocks(1); /*bust_spinlocks()机制,用来避免系统crash以至于无法正常工作的时候还要进行不必要的等待spinlock操作*/
va_start(args, fmt);
/* 利用va_start指针遍历函数栈方式处理多参数
* 函数栈: (高地址)|-最后一个参数--... ...--第一个参数-|---返回地址---|---函数执行代码- --|(低地址)
*/
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
#ifdef CONFIG_DEBUG_BUGVERBOSE /* 是否输出BUG行号和文件名 */
/*
* Avoid nested stack-dumping if a panic occurs during oops processing
*/
if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
dump_stack(); /*打印堆栈*/
#endif
/*
* If we have crashed and we have a crash kernel loaded let it handle
* everything else.
* Do we want to call this before we try to display a message?
*/
crash_kexec(NULL);/* 内核crash的操作 */
kmsg_dump(KMSG_DUMP_PANIC);/*kmsg日志保存到mtd设备去,需要提前分配好mtd存储*/
/*
* Note smp_send_stop is the usual smp shutdown function, which
* unfortunately means it may not be hardened to work in a panic
* situation.
*/
smp_send_stop(); /* 停止其他多核的工作 */
atomic_notifier_call_chain(&panic_notifier_list, 0, buf);/*注册了异常handle的函数都调用一次*/
bust_spinlocks(0);
if (!panic_blink)
panic_blink = no_blink;
if (panic_timeout > 0) {
/*
* Delay timeout seconds before rebooting the machine.
* We can't use the "normal" timers since we just panicked.
*/
printk(KERN_EMERG "Rebooting in %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);
}
}
if (panic_timeout != 0) {
/*
* This will not be a clean reboot, with everything
* shutting down. But if there is a chance of
* rebooting the system it will be rebooted.
*/
emergency_restart();
}
/**
* panic_timeout = 0 进行死循环,抢占被禁止,CPU一直运行panic程序中运行。
*/
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);
}
}
EXPORT_SYMBOL(panic);
hung task相关配置
- 设置timeout时间:
echo xx > /proc/sys/kernel/hung_task_timeout_secs
xx单位为s。 - 设置hung task后是否触发panic
echo 1 > /proc/sys/kernel/hung_task_panic
当前解决措施
因为自动化流程中是故意将文件系统设置为只读,正常情况下大概率会触发hung task 导致panic,所以直接设置为不触发panic,避免系统挂起,即:
echo 0 > /proc/sys/kernel/hung_task_panic
评论区