系统的PANIC"/>
系统的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);
}
更多推荐
系统的PANIC
发布评论