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);
}