linux 内核die函数,内核异常分析方法

本文详细介绍了Linux内核出现异常时的分析方法,特别是通过die函数产生的oops信息。讲解了如何利用EIP、堆栈跟踪和代码段来定位错误代码,包括在有debuginfo和无debuginfo情况下的处理方式。同时讨论了不同类型的异常,如无效操作码、页异常等,并提到了关键配置选项如CONFIG_KALLSYMS和CONFIG_DEBUG_INFO的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

=============================================================

内核异常分析方法

=============================================================

-------------------------------------------------------------

1 总体方法

-------------------------------------------------------------

1.1 确定出错函数地址

1.2 确定出错地址在函数中的偏移

-------------------------------------------------------------

2 oops信息(由die函数产生)

-------------------------------------------------------------

/* This is gone through when something in the kernel

* has done something bad and is about to be terminated.

*/

void die(const char * str, struct pt_regs * regs, long err)

{

static struct {

spinlock_t lock;

u32 lock_owner;

int lock_owner_depth;

} die = {

.lock =     SPIN_LOCK_UNLOCKED,

.lock_owner =    -1,

.lock_owner_depth = 0

};

static int die_counter;

unsigned long flags;

oops_enter();

if (die.lock_owner != raw_smp_processor_id()) {

console_verbose();

spin_lock_irqsave(&die.lock, flags);

die.lock_owner = smp_processor_id();

die.lock_owner_depth = 0;

bust_spinlocks(1);

}

else

local_save_flags(flags);

if (++die.lock_owner_depth < 3) {

int nl = 0;

unsigned long esp;

unsigned short ss;

handle_BUG(regs); //对应oops信息的第一二行,指出出错位置所在的文件,以及行号,有了这个信息,就可以直接找到c源代码

printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);//这里的str对应invalid opcode,是在调用die时传入的值,表示错误类型,err表示这类型具体的错误号,die_counter表示调用die的次数

#ifdef CONFIG_PREEMPT

printk(KERN_EMERG "PREEMPT "); //抢占版本内核

nl = 1;

#endif

#ifdef CONFIG_SMP

if (!nl)

printk(KERN_EMERG);

printk("SMP ");          //多核版本内核

nl = 1;

#endif

#ifdef CONFIG_DEBUG_PAGEALLOC

if (!nl)

printk(KERN_EMERG);

printk("DEBUG_PAGEALLOC");

nl = 1;

#endif

if (nl)

printk("\n");

#ifdef CONFIG_SYSFS

printk(KERN_ALERT "last sysfs file: %s\n", last_sysfs_file);

#endif

if (notify_die(DIE_OOPS, str, regs, err,

current->thread.trap_no, SIGSEGV) !=

NOTIFY_STOP) {

show_registers(regs); //打印了图中的大部分信息

/* Executive summary in case the oops scrolled away */

esp = (unsigned long) (&regs->esp);

savesegment(ss, ss);

if (user_mode(regs)) {

esp = regs->esp;

ss = regs->xss & 0xffff;

}

printk(KERN_EMERG "EIP: [] ", regs->eip);

print_symbol("%s", regs->eip);

printk(" SS:ESP %04x:%08lx\n", ss, esp);  //这三行打印最后一行

}

else

regs = NULL;

} else

printk(KERN_EMERG "Recursive die() failure, output suppressed\n");

bust_spinlocks(0);

die.lock_owner = -1;

spin_unlock_irqrestore(&die.lock, flags);

if (!regs)

return;

if (kexec_should_crash(current))

crash_kexec(regs);

if (in_interrupt())

panic("Fatal exception in interrupt");

if (panic_on_oops)

panic("Fatal exception");

oops_exit();

do_exit(SIGSEGV);

}

void show_registers(struct pt_regs *regs

### Linux 内核中的异常类型及其分类 在 Linux 内核环境中,异常是指 CPU 在执行过程中遇到某些特殊情况而触发的事件。这些情况可以分为两大类:中断 (Interrupts) 和例外 (Exceptions)[^1]。 #### 中断 (Interrupts) 中断是由外部硬件设备发起的信号,用来通知处理器有需要立即处理的任务。这类事件通常是异步发生的,意味着它们可以在任何时间点打断当前正在运行的程序流程。常见的中断源包括定时器、键盘输入和其他 I/O 设备等。 - **可屏蔽中断 (Maskable Interrupt, IRQ)**: 可以通过设置特定寄存器位来允许或阻止这种类型的中断响应。 - **不可屏蔽中断 (Non-Maskable Interrupt, NMI)**: 不受常规中断控制机制的影响,即使是在系统处于调试状态时也会被处理。 #### 例外 (Exceptions) 例外则是由CPU内部产生的同步错误条件引起的一系列问题,当指令试图访问非法地址或者发生除零等情况时就会产生此类异常。根据其性质的不同,又细分为以下几种类别: - **故障 (Faults)**: 这是最常见的一种形式,在大多数情况下是可以恢复并继续正常执行下去的;例如页面缺失(Page Fault),它表示尝试访问的数据不在物理内存中,此时操作系统会加载所需数据到RAM后再重新启动引发此故障的操作。 - **陷阱 (Traps)**: 故意设计成让软件能够主动请求服务的方式之一,比如调用系统API函数或是实现断点(Debugger Breakpoint)功能的时候就可能会用到这种方式。 - **终止 (Aborts)**: 表明遇到了严重的错误状况以至于无法安全地完成后续工作,因此不得不强制结束进程甚至重启整个机器。典型的例子像总线错误(Bus Error)以及存储保护违例(Storage Protection Violation)都属于这种情况。 对于上述提到的各种异常情形,Linux 内核都有相应的处理逻辑去应对每一种可能的情况,从而确保系统的稳定性和可靠性。 ```c void handle_exception(struct pt_regs *regs) { int vector = regs->orig_rax; /* 获取异常向量 */ switch(vector){ case PAGE_FAULT: do_page_fault(regs); break; case SYSTEM_CALL: sys_call_handler(regs); break; default: die("Unhandled Exception", regs, vector); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值