系统的PANIC

panic并没有什么大不了的,就是禁抢占,善后,
禁止了抢占实际上就禁止了调度,因为禁止抢占的情况下除非自己放弃cpu才能调度,
但是我们看看那个死循环根本没有放弃cpu的意思。
发生panic无非就两种情形,一个是在中断上下文,另一个是在非中断上下文。
在中断上下文中 panic的话,此时的in_interrupt()一定返回真,如果此硬件中断触发了一个软中断,那么这个软中断是永远不会被执行的,
因为导致 panic的中断永远不会调用irq_exit来使得in_interrupt()返回假。
这就解释了为何在中断中panic的系统ping不通,而在非中断上下文中panic虽然内核死掉了但是还是可以从外部ping通的。
想想ping的执行,ping依靠的是icmp协议,没有进程上下文,它是ip层的内容,而ip层是不区分进程的,
于是icmp的处理只在软中断上下文进行,既然硬件中断中panic了,根据上面的论述,软中断就没有机会执行了,
于是就ping不通了;而非中断的panic由于软中断可以执行,所以可以ping通,但是内核还是不能恢复了,
因为已经关闭可抢占,无法调度了,于是执行流出去中断后便会一直执行那个死循环,没有机会执行别的。


目前有三种记录的方式: kdump; mtdoops; crashlog(这是openwrt特别的功能,正式linux内核中没有)
大家对kdump比较了解。它主要使用于x86系统。因为它使用占用大量内存和硬盘。
kdump是一种先进的基于kexec的内核崩溃转储机制。当系统崩溃时,kdump使用kexec 启动到第二个内核。第二个内核通常叫做捕获内核,以很小内存启动以捕获转储镜像。
mtdoops和crashlog主要用于嵌入式的环境。也只是记录文本日志。

SSD在硬件内部的微代码中实现了wear leveling等分布写操作的技术,因此系统无须再使用特殊的MTD驱动和FTL层

系统重启原因:

    软件故障:在设置了panic参数时,panic会触发重启,不掉电重启,这时候panic会打印栈调用信息,
        不论是否掉电,串口和显示器都会有信息;掉电那么执行命令3就会是空的
    
    硬件故障:这种很难判断原因,需要硬件厂家的协助复现。
    
    硬件狗:喂狗程序自己有问题,那也会触发重启,只能关掉硬件狗再看(命令2)。
    

===背景知识==

panic:
    引起原因通常两种:
        非法内存访问 (比如访问地址0)
        非法指令
        
    内核进入恐慌,执行以下动作:
        关闭抢占-->栈调用信息-->停止别的cpu-->panic通知链-->kdump-->倒计时重启-->死循环
        
        栈调用信息我们是放到内存sg_res_mem,系统启动会保存到对应文件
            /media/debug/log/sgkdump.log,不过掉电重启消失
        /proc/sys/kernel/panic 表示重启倒计时的时间,为0则不重启,但此时CPU无法调度了
        kdump使用kexec启动到第二个内核,方便查看panic信息,占用空间较大,没有设置
        
    发生位置
        非中断上下文,从外部可以ping通
        中断上下文

panic_on_oops:
    内核惊叹不一定进入panic,可以通过参数控制/proc/sys/vm/panic_on_oom
    中断上下文,oops必须抛panic
    进程上下文,可以只打印oops

lockup
    由于某种原因导致系统处于内核态超过20s导致进程无法运行(soft lockup)
    由于某种原因导致系统处于内核态超过10s导致中断无法运行(hard lockup)
    
soft lockup
    系统会为每个cpu core注册一个一般的kernel线程,名字叫watchdog/0,watchdog/1…以此类推。
    这个线程会定期得调用watchdog函数,某个cpu上超过20s没有执行,更新watchdog_touch_ts变量

hard lockup
    基于PMU的NMI perf event,当PMU的计数器溢出时会触发NMI中断,因为NMI中断是不可屏蔽的
    在中断中去检查hrtimer_interrupts变量
    
softlockup_panic
    参数让机器发生softlock后进入panic

2.6内核中:
    softlockup_thresh = kernel.watchdog_thresh,默认60秒;

3.10内核中:
    kernel.watchdog_thresh = hard lockup threshold,默认10秒;
    soft lockup threshold = 2*kernel.watchdog_thresh),即默认20秒。


kernel/panic.c

#################### insmod hello.ko #################

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
        printk(KERN_ALERT "Hello, world !/n");
        return 0;
}
static void hello_exit(void)
{
        printk(KERN_ALERT "Goodbye, cruel world !/n");
}
module_init(hello_init);
module_exit(hello_exit);
#######################  makefile  ###################
obj-m := hello.o
KERNELDR := /usr/src/linux
PWD := $(shell pwd)
modules:
    $(MAKE) -C $(KERNELDR) M=$(PWD) modules
moduels_install:
    $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install
clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
#########################  notify.c  #############################
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/syscalls.h>

#include <linux/fs.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>

#include <linux/file.h>
MODULE_LICENSE("Dual BSD/GPL");

char log_file[100] = "/media/boot/panic.log";
char init_msg[100] = "NOTIFY START\n";
char panic_msg[100] = "KERNEL PANIC\n";
int i;
int count;
struct file *phMscd_Filp = NULL;
mm_segment_t old_fs;

// ****************************************************************************
// Here is where i receive the panic notification (from panic.c)
// ****************************************************************************

static int record_msg(char *msg, int len)
{
    int ret = 0;
    printk("############## start record panic ###########\n");
    printk("msg: %s, len : %d\n", msg ,len);

    // request file
    phMscd_Filp = filp_open(log_file, O_RDWR | O_LARGEFILE | O_CREAT | O_APPEND , 0);
    if (phMscd_Filp == NULL) {
        printk("filp_open error!!.\n");
        return 0;
    }

    // save actual space
    printk("############## save actual space ###########\n");
    old_fs=get_fs();

    // jump to other space
    printk("############## jump to other space ###########\n");
    set_fs(get_ds());

    // write the file
    printk("############## write the file ###########\n");
    ret = vfs_write(phMscd_Filp, msg, len, &phMscd_Filp->f_pos);
    if (ret)
        printk("############## ret: %d ###########\n", ret);

    // close the file
    printk("############## close the file ###########\n");
    filp_close(phMscd_Filp,NULL);

    // back to old space
    printk("############## back to old space ###########\n");
    set_fs(old_fs);
    return 0;
}
static int panic_happened(struct notifier_block *n, unsigned long val, void *message)
{
    return record_msg(panic_msg, sizeof(panic_msg));
}

// ****************************************************************************
// block
// ****************************************************************************
static struct notifier_block panic_notifier = { panic_happened, NULL, 1 };

// ****************************************************************************
// load
// ****************************************************************************
static int __init register_my_panic(void)
{
    atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier);
    printk("register panic notify\n");
    record_msg(init_msg, sizeof(init_msg));
    return 0;
}

// ****************************************************************************
// unload
// ****************************************************************************
static void __exit unregister_my_panic(void)
{
    atomic_notifier_chain_unregister(&panic_notifier_list, &panic_notifier);
    printk("unregister panic notify");
}

module_init(register_my_panic);
module_exit(unregister_my_panic);

--------------------------------------------------------------------------------
A simple way to dump kmsg into mmc storage
--------------------------------------------------------------------------------
kernel space
--------------------------------------------------------------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/vmalloc.h>
#include <linux/seq_file.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kmsg_dump.h>
#include <linux/proc_fs.h>
 
static struct kmsg_dumper dump;
static struct proc_dir_entry *my_proc;
static int is_panic = 0;
 
static int my_proc_show(struct seq_file *m, void *v)
{
    seq_printf(m, "%d", is_panic);
    return 0;
}
static int my_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, my_proc_show, NULL);
}
static const struct file_operations my_proc_ops = {
    .open        = my_proc_open,
    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = single_release,
};
static void oops_do_dump(struct kmsg_dumper *dumper,
        enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
        const char *s2, unsigned long l2)
{
    int i;
    printk("### [%s:%d] reason = %d\n", __func__, __LINE__, reason);
    is_panic = 1;
 
    for (i = 0;i < 10; i++)
        msleep(1000);
    printk("### [%s:%d] should be done\n", __func__, __LINE__);
}
static int __init my_oops_init(void)
{
    int err;
 
    dump.dump = oops_do_dump;
    err = kmsg_dump_register(&dump);
    if (err) {
        printk(KERN_ERR "oops: registering kmsg dumper failed, error %d\n", err);
        return -EINVAL;
    }
    my_proc = proc_create("dump_tester", 0, NULL, &my_proc_ops);
 
    return 0;
}
static void __exit my_oops_exit(void)
{
    printk("### [%s:%d]\n", __func__, __LINE__);
    if (my_proc)
        remove_proc_entry( "dump_tester", NULL);
    kmsg_dump_unregister(&dump);
 
}
module_init(my_oops_init);
module_exit(my_oops_exit);
MODULE_LICENSE("GPL");
--------------------------------------------------------------------------------
User space
--------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
 
#define BUF_LEN 40960
 
 
void main(int argc, char **argv)
{
    char tmp = 'X';
    char buf[BUF_LEN];
    int fd_src, fd_trg;
    int fd = open("/proc/dump_tester", O_RDONLY, 0);
    while(1) {
        lseek(fd, 0, SEEK_SET);
        read(fd, &tmp, 1);
        //printf("### [%s:%d] ==> '%c'\n", __FUNCTION__, __LINE__, tmp);
 
        if (tmp == '1') {
            fd_src = open("/proc/kmsg", O_RDONLY, 0);
            fd_trg = open("/dev/block/mmcblk0p6",  O_RDWR, 0);
            memset(buf, 0, BUF_LEN);
            write(fd_trg, buf, BUF_LEN);
            lseek(fd_trg, 0, SEEK_SET);
 
            read(fd_src, buf, BUF_LEN);
            write(fd_trg, buf, BUF_LEN);
            close(fd_src);
            close(fd_trg);
            sleep(1);
            printf("### dump panic log into %s\n", "/dev/block/mmcblk0p6");
            break;
        }
        sleep(1);
    }
    close(fd);
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值