Linux panic内核恐慌与kmsg_dump注册回调
panic是Linux内核的终极异常处理函数。当内核检测到无法恢复的错误时,调用panic()终止系统运行。panic()的实现位于kernel/panic.c中,承载了信息转储、notifier回调、kmsg_dump输出和最终停机等一系列操作。
__setup("panic=", panic_setup)允许内核启动参数配置panic_timeout,控制自动重启的延迟秒数:
void panic(const char *fmt, ...)
{
static DEFINE_SPINLOCK(panic_lock);
static char buf[1024];
va_list args;
long i;
int state = 0;
int old_cpu;
/*
* 防止多个CPU同时进入panic:只有第一个进入panic的CPU
* 继续执行,其他CPU在panic_smp_self_stop中自旋
*/
old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID,
raw_smp_processor_id());
if (old_cpu != PANIC_CPU_INVALID)
panic_smp_self_stop();
/* 获取panic全局锁,序列化输出 */
if (spin_trylock(&panic_lock)) {
pr_emerg("Kernel panic - not syncing: ");
va_start(args, fmt);
vprintk_emit(0, LOGLEVEL_EMERG, NULL, fmt, args);
va_end(args);
}
/*
* 调用panic_notifier_list上的所有回调。
* 这些回调由panic_notifier_chain_register注册,
* 典型的注册者包括kdump、netconsole、DRM等
*/
atomic_notifier_call_chain(&panic_notifier_list, 0, buf);
/* 触发kmsg_dump:将内核日志转储到预先注册的dump设备 */
kmsg_dump(KMSG_DUMP_PANIC);
/*
* 输出附加信息:堆栈、寄存器、已加载模块等
*/
bust_spinlocks(0);
printk("---[ end Kernel panic - not syncing ]---\n");
/* 等待panic_timeout秒后重启 */
if (panic_timeout > 0) {
pr_emerg("Rebooting in %d seconds..\n", panic_timeout);
for (i = 0; i < panic_timeout; i++) {
touch_nmi_watchdog();
mdelay(1000);
}
emergency_restart();
}
/* 无限等待用户手动重启 */
local_irq_enable();
while (1)
cpu_relax();
}
kmsg_dump机制是panic流程中最重要的输出通道之一。设备驱动或平台代码通过kmsg_dump_register注册dump器:
int kmsg_dump_register(struct kmsg_dumper *dumper)
{
unsigned long flags;
int err = -EBUSY;
spin_lock_irqsave(&dump_list_lock, flags);
if (!dumper->registered) {
dumper->registered = true;
dumper->max_reason = KMSG_DUMP_MAX;
list_add_tail_rcu(&dumper->list, &dump_list);
err = 0;
}
spin_unlock_irqrestore(&dump_list_lock, flags);
return err;
}
struct kmsg_dumper结构包含核心的dump回调:
struct kmsg_dumper {
struct list_head list;
void (*dump)(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *s1, unsigned long l1,
const char *s2, unsigned long l2);
enum kmsg_dump_reason max_reason;
bool registered;
bool active;
};
当panic调用kmsg_dump(KMSG_DUMP_PANIC)时,遍历dump_list上的所有注册dump器:
void kmsg_dump(enum kmsg_dump_reason reason)
{
struct kmsg_dumper *dumper;
unsigned long flags;
rcu_read_lock();
list_for_each_entry_rcu(dumper, &dump_list, list) {
if (dumper->max_reason <= reason)
continue;
/* 防止dump回调中的死循环 */
if (dumper->active)
continue;
dumper->active = true;
/* 从printk环形缓冲区迭代读取日志 */
kmsg_dump_get_buffer(dumper, true,
dumper->dump_buf,
sizeof(dumper->dump_buf),
&len);
if (len > 0)
dumper->dump(dumper, reason,
dumper->dump_buf, 0,
NULL, 0);
dumper->active = false;
}
rcu_read_unlock();
}
典型的kmsg_dump实现者包括:
1. MTD/Flash dump:在嵌入式设备上将panic日志写入flash的最后一个分区
2. pstore/ramoops:将panic日志保存在保留的RAM区域中,重启后通过/pstore文件系统读取
3. netconsole:将panic日志通过网络发送到远程日志服务器
pstore的ramoops实现通过kmsg_dump_register注册回调,其dump函数将日志写入预分配的物理内存区域:
static void ramoops_pstore_write(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *s1, unsigned long l1,
const char *s2, unsigned long l2)
{
struct ramoops_context *cxt = container_of(dumper,
struct ramoops_context, dump);
size_t hlen = sizeof(struct persistent_ram_header);
struct persistent_ram_zone *prz;
if (reason != KMSG_DUMP_PANIC &&
reason != KMSG_DUMP_OOPS)
return;
prz = cxt->przs[cxt->dump_write_cnt % cxt->max_dump_cnt];
persistent_ram_write(prz, s1, l1);
if (s2)
persistent_ram_write(prz, s2, l2);
cxt->dump_write_cnt++;
}
panic的另一个重要路径是crash_kexec。如果系统配置了kdump,panic_notifier的回调会调用crash_kexec来加载捕获内核:
static int kdump_panic_callback(struct notifier_block *self,
unsigned long val, void *data)
{
crash_kexec(NULL);
return NOTIFY_DONE;
}
kdump机制在内核崩溃时启动一个预加载的capture kernel,该内核在原内核预留的内存区域中运行,收集崩溃现场的/proc/vmcore。
Linux panic内核恐慌与kmsg_dump注册回调
张小明
前端开发工程师
如何快速解决本地音乐歌词缺失问题:LRCGET批量歌词下载完整实战指南
如何快速解决本地音乐歌词缺失问题:LRCGET批量歌词下载完整实战指南 【免费下载链接】lrcget Utility for mass-downloading LRC synced lyrics for your offline music library. 项目地址: https://gitcode.com/gh_mirrors/lr/lrcget 你是否拥有大量本地音乐…
MPC8309通信处理器实战:从核心架构到驱动开发与调试
1. 从手册到实战:深度解析MPC8309通信处理器的核心架构与设计哲学如果你是一名嵌入式网络设备开发者,或者正在为你的工业控制、智能网关项目寻找一颗高集成度、高性价比的通信处理器核心,那么飞思卡尔(现恩智浦)的Powe…
ATM IMA技术原理与MPC8323E实现详解
1. 项目概述:ATM IMA技术与MPC8323E的深度耦合在宽带接入、企业网络回程以及某些传统的电信传输场景中,我们常常会遇到一个经典矛盾:上层应用(比如一个高速的ATM虚电路)需要稳定且较高的带宽,但底层可用的物…
MPC8245硬件手册勘误解析:从PCI配置到SDRAM时序的避坑指南
1. 项目概述与勘误背景在嵌入式系统开发,尤其是基于PowerPC架构的早期网络通信、工控设备设计中,一份准确无误的硬件参考手册就是工程师的“圣经”。我最近在为一个老旧的通信网关设备进行维护和功能升级,其核心处理器正是Freescaleÿ…
Unity游戏去马赛克终极指南:3分钟恢复完整视觉体验
Unity游戏去马赛克终极指南:3分钟恢复完整视觉体验 【免费下载链接】UniversalUnityDemosaics A collection of universal demosaic BepInEx plugins for games made in Unity3D engine 项目地址: https://gitcode.com/gh_mirrors/un/UniversalUnityDemosaics …
MPC8245中断控制器与串口驱动开发实战:从寄存器配置到系统集成
1. 项目概述与核心价值如果你正在开发基于MPC8245这类PowerPC架构的嵌入式系统,那么对其中断控制器(PIC)和串口(DUART)的深入理解,绝对是绕不开的硬核课题。这不仅仅是看懂手册里那一堆寄存器缩写ÿ…