Linux内核(十七)Linux内核 panic函数 详解以及实际应用

文章目录

    • panic 函数详解
    • 实际应用
    • Bug调试


Linux 版本:Linux version 3.18.24
panic函数原型路径在:kernel/panic.c

panic 函数详解

panic函数的主要作用是停止当前的系统运行,在系统监测到异常时调用该函数。

void panic(const char *fmt, ...)
    -> local_irq_disable();        // 关闭本地中断,防止任务抢占
    // 有可能直接从恐慌断言来到这里,而不禁用先发制人。从这里调用的一些函数希望禁用抢占。不过,以后再启用它也没有意义…只允许一个CPU从这里执行紧急代码。对于多个并行panic调用,所有其他CPU要么停止自己,要么等待第一个CPU使用smp send stop()停止它们。
    -> if (!spin_trylock(&panic_lock))
            panic_smp_self_stop();
    -> console_verbose();        // 修改console 级别,以便printk能把消息打印出来。
    -> smp_send_stop();           // smp关闭函数
    -> atomic_notifier_call_chain(&panic_notifier_list, 0, buf);    // 通知运行panic处理函数
    -> kmsg_dump(KMSG_DUMP_PANIC);        // dump CPU寄存器的状态值已经一些堆栈的信息输出
    -> if (panic_timeout > 0) {        // 如果sysctl配置了panic_timeout > 0则在panic_timeout后重       * 启系统
            pr_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);
            }
        }
    -> local_irq_enable();            // 本地锁打开,进行死循环,抢占被禁止,CPU一直运行panic程序中运行。
        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);
        }

实际应用

了解上面panic函数具体行为之后,就可以利用panic函数做一些log记录功能等。

现象: 小伙伴有没有遇到这样一个场景。当用户手上的系统设备没有接上串口,此时系统发生了不知名的panic 崩溃,看门狗重启了。这时再去分析查看panic 信息log已经消失了。

解决方案: 小编在项目中遇到这种情况,利用panic 函数将系统重启之前有必要的一些打印保存下来再重启。这就保证看门狗重启之后,我还能查看到系统重启之前的相关系统环境。

结合 Linux内核(十六)Linux 内核态进行读写文件的函数 使用和解析文章实现log记录。

有需要的小伙伴领取代码: Linux内核系统 panic log 记录代码
(注:如果要将panic信息保存在文件系统里,在系统重启后可以查看这个panic。可以在打印堆栈信息之后,添加该函数将信息存在文件系统中。)

log保存在文件系统中,效果图:
Linux内核(十七)Linux内核 panic函数 详解以及实际应用_第1张图片
在启动脚本中可以限制log个数,不至于log信息堆积影响系统正常运行(实例中只保留最新的5个panic log)

panic_file="/mnt/log"
if [ -d "$panic_file" ];then
    if [ $(ls -l /mnt/log/ |grep "^-"|wc -l ) -gt 6 ]; then
        cd /mnt/log
        rm `ls -t  |tail -n +6`
    fi
else
    mkdir /mnt/log
fi

Bug调试

在调试的过程中出现一个bug:使用vfs_write写文件出现失败
Linux内核(十七)Linux内核 panic函数 详解以及实际应用_第2张图片
源码中写文件

size_t ret = 0;
ret = vfs_read(fkmsg, p, size, &pos_kmsg)
n = vfs_write(ffile, p, ret, &pos_file);

原因:
vfs_write和vfs_read返回值是ssize_t类型,传入参数是size_t类型

size_t: 是标准C库中定义的,32位系统:unsigned int,在64为系统: long unsigned int。
在C++中,设计 size_t 就是为了适应多个平台的,增强可移植性。
在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节.

ssize_t:
这个数据类型用来表示可以被执行读写操作的数据块的大小。它和size_t类似,但必需是signed.意即:它表示的是signed
size_t类型的(typedef signed int size_t)。 改成下面的代码就能解决问题:

改成下面的代码就能解决问题:

ssize_t ret = 0;
ret = vfs_read(fkmsg, p, size, &pos_kmsg)
n = vfs_write(ffile, p, (size_t)ret, &pos_file);

你可能感兴趣的:(Linux内核,linux,运维,panic,log)