博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux内核的oops信息
阅读量:4051 次
发布时间:2019-05-25

本文共 4930 字,大约阅读时间需要 16 分钟。

linux内核的oops信息

 
 
 
Oops可看成是内核级(特权级)的Segmentation Fault。一般应用程序(用户级)如进行了内存的非法访问(地址不合法、无权限访问、……)或执行了非法指令,则会得到Segfault信号,一般对应的行为是coredump,应用程序也可以自行获取Segfault信号进行处理,而内核出错则是打印出Oops信息。
 
 
 
内核打印Oops信息的执行流程:
 
 
1、do_page_fault()(arch/i386/mm/fault.c),如果内核出现非法访问,则该函数会打印出EIP、PDE等信息,如下:
 
 
 
Unable to handle kernel paging request at virtual address f899b670
 
 
 
printing eip:
 
 
 
c01de48c
 
 
 
*pde = 00737067
 
 
接下来调用die("Oops", regs, error_code);函数,此时如果系统还活着(至少要满足两个条件:1. 在进程上下文 2. 没有设置panic_on_oops),则会kill掉当前进程,以致死机。
 
 
2、die()(arch/i386/kernel/traps.c),该函数最开始会打印出:
 
 
 
Oops: 0002 [#1]
 
 
 
其中,0002代表错误码,#1代表Oops发生次数。
 
 
 
error_code:
 
 
 
* bit0  
 
 
0 means no page found, 1 means protection fault
 
 
 
* bit1  
 
 
0 means read, 1 means write
 
 
 
* bit2  
 
 
0 means kernel, 1 means user-mode
 
 
 
* bit3  
 
 
0 means data,1 means instruction
 
 
 
接下来会调用 show_registers(regs) 函数,输出寄存器、当前进程、堆栈、指令代码等信息,以供判断。
 
 
 
Linux内核在发生kernel panic时会打印出Oops信息,把当前的寄存器状态、堆栈信息、完整的Call trace都打印出来,以帮助我们定位错误。下在是一个例子,该例子展示了空指针引用错误。
 
 
 
01  
#include <linux/kernel.h>
 
 
 
02  
#include <linux/module.h>
 
 
 
03 
 
 
 
04  
static int __init hello_init(void)
 
 
 
05  
{
 
 
 
06  
 
int *p = 0;
 
 
 
07  
 
 
 
 
 
08  
 
*p = 1;
 
 
 
09  
 
return 0;
 
 
 
10  
}
 
 
 
11
 
 
 
12  
static void __exit hello_exit(void)
 
 
 
13  
{
 
 
 
14  
 
return;
 
 
 
15  
}
 
 
 
16
 
 
 
17  
module_init(hello_init);
 
 
 
18  
module_exit(hello_exit);
 
 
 
19
 
 
 
20  
MODULE_LICENSE("GPL");
 
 
 
从上面的代码中,我们可以很容易看到出错的代码在08行,当我们把它编译成一个*.ko模块,并使用insmod将其添加到内核时,Oops信息如期而至,如下:
 
 
 
[  
100.243737] BUG: unable to handle kernel NULL pointer dereference at (null)
 
 
 
[  
100.244985] IP: [<f82d2005>] hello_init+0x5/0x11 [hello]
 
 
 
[  
100.262266] *pde = 00000000 
 
 
 
[  
100.288395] Oops: 0002 [#1] SMP 
 
 
 
[  
100.305468] last sysfs file: /sys/devices/virtual/sound/timer/uevent
 
 
 
[  
100.325955] Modules linked in: hello(+) vmblock vsock vmmemctl vmhgfs acpiphp snd_ens1371 gameport snd_ac97_codec ac97_bus snd_pcm_oss 
snd_mixer_oss snd_pcm snd_seq_dummy snd_seq_oss snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq snd_timer snd_seq_device ppdev psmouse serio_raw 
fbcon tileblit font bitblit softcursor snd parport_pc soundcore snd_page_alloc vmci i2c_piix4 vga16fb vgastate intel_agp agpgart shpchp lp parport 
floppy pcnet32 mii mptspi mptscsih mptbase scsi_transport_spi vmxnet
 
 
 
[  
100.472178] [  
100.494931] Pid: 1586, comm: insmod Not tainted (2.6.32-21-generic #32-Ubuntu) VMware Virtual Platform
 
 
 
[  
100.540018] EIP: 0060:[<f82d2005>] EFLAGS: 00010246 CPU: 0
 
 
 
[  
100.562844] EIP is at hello_init+0x5/0x11 [hello]
 
 
 
[  
100.584351] EAX: 00000000 EBX: fffffffc ECX: f82cf040 EDX: 00000001
 
 
 
[  
100.609358] ESI: f82cf040 EDI: 00000000 EBP: f1b9ff5c ESP: f1b9ff5c
 
 
 
[  
100.631467]  
DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
 
 
 
[  
100.657664] Process insmod (pid: 1586, ti=f1b9e000 task=f137b340 task.ti=f1b9e000)  
 
 
 
[  
100.706083] Stack:
 
 
 
[  
100.731783]  
f1b9ff88 c0101131 f82cf040 c076d240 fffffffc f82cf040 0072cff4 f82d2000
 
 
 
[  
100.759324] <0> fffffffc f82cf040 0072cff4 f1b9ffac c0182340 f19638f8 f137b340 f19638c0
 
 
 
[  
100.811396] <0> 00000004 09cc9018 09cc9018 00020000 f1b9e000 c01033ec 09cc9018 00015324
 
 
 
[  
100.891922] Call Trace:
 
 
 
[  
100.916257]  
[<c0101131>] ? do_one_initcall+0x31/0x190
 
 
 
[  
100.943670]  
[<f82d2000>] ? hello_init+0x0/0x11 [hello]
 
 
 
[  
100.970905]  
[<c0182340>] ? sys_init_module+0xb0/0x210
 
 
 
[  
100.995542]  
[<c01033ec>] ? syscall_call+0x7/0xb
 
 
 
[  
101.024087] Code: <c7> 05 00 00 00 00 01 00 00 00 5d c3 00 00 00 00 00 00 00 00 00 00 
 
 
 
[  
101.079592] EIP: [<f82d2005>] hello_init+0x5/0x11 [hello] SS:ESP 0068:f1b9ff5c
 
 
 
[  
101.134682] CR2: 0000000000000000
 
 
 
[  
101.158929] ---[ end trace e294b69a66d752cb ]---
 
 
Oops描述了Bug类型,并指出Bug的位置,即“IP: [<f82d2005>] hello_init+0x5/0x11 [hello]”。此时,我们需要用objdump工具来帮忙分析问题,该命令可以帮助反汇编,执行命令如下:
 
 
objdump -S  
hello.o
 
 
下面是反汇编后的内容,是C语言与汇编混合代码,如下:
 
 
01  
hello.o:  
 
file format elf32-i386
 
 
02
 
 
03
 
 
04  
Disassembly of section .init.text:
 
 
05
 
 
06  
00000000 <init_module>:
 
 
07  
#include <linux/kernel.h>
 
 
08  
#include <linux/module.h>
 
 
09
 
 
10  
static int __init hello_init(void)
 
 
11  
{
 
 
12  
0:  
55  
 
 
 
 
 
 
 
 
 
 
push  
% ebp
 
 
13  
 
int *p = 0;
 
 
14  
 
 
 
15  
 
*p = 1;
 
 
16  
 
 
 
 
17  
 
return 0;
 
 
18  
}
 
 
19  
1:  
31 c0  
 
 
 
 
 
 
 
 
xor  
 
% eax,% eax
 
 
20  
#include <linux/kernel.h>
 
 
21  
#include <linux/module.h>
 
 
22 
 
 
23  
static int __init hello_init(void)
 
 
24  
{
 
 
25  
3:  
89 e5  
 
 
 
 
 
 
 
 
mov  
 
%esp,% ebp
 
 
26  
 
int *p = 0;
 
 
27
 
 
28  
 
*p = 1;
 
 
29  
5:  
c7 05 00 00 00 00 01  
 
movl  
$0x1,0x0
 
 
30  
c:  
00 00 00
 
 
31  
 
 
 
32  
 
return 0;
 
 
33  
}
 
 
34  
f:  
5d  
 
 
 
 
 
 
 
 
 
 
pop  
 
% ebp
 
 
35  
10:  
c3  
 
 
 
 
 
 
 
 
 
 
ret  
 
 
 
36
 
 
37  
Disassembly of section .exit.text:
 
 
38
 
 
39  
00000000 <cleanup_module>:
 
 
40
 
 
41  
static void __exit hello_exit(void)
 
 
42  
{
 
 
43  
0:  
55  
 
 
 
 
 
 
 
 
 
 
push  
% ebp
 
 
44  
1:  
89 e5  
 
 
 
 
 
 
 
 
mov  
 
%esp,% ebp
 
 
45  
3:  
e8 fc ff ff ff  
 
 
 
 
call  
4 <cleanup_module+0x4>
 
 
46  
 
return;
 
 
47  
}
 
 
48  
8:  
5d  
 
 
 
 
 
 
 
 
 
 
pop  
 
% ebp
 
 
49  
9:  
c3  
 
 
 
 
 
 
 
 
 
 
ret  
 
 
 
 
(注意:上面的% ebp等中间出现空格,其中的空格应去掉,因为sina作了处理,故采用空格跳过)
 
 
 
对照Oops的提示,我们可以很清楚的看到,出错的位置hello_init+0x5的汇编代码是:
 
 
 
29  
5:c7 05 00 00 00 00 01 movl  
$0x1,0x0
 
 
 
这句代码的作用是把数值1存入0这个地址,这个操作当然是非法的,同时也可以看到对应的源码为:
 
 
 
28  
*p = 1;
 
 
 
哈哈,在Oops信息的帮助下,我们很快就可以找到问题所在。
 
 
 
该例子没有造成死机,可以使用dmesg命令查看到完整的错误信息,但很多时候是会造成死机,并且会存在多屏显示提示信息,那么我们可以使用内核转储工具kdump把发生Oops时的内存和CPU寄存器的内容dump到一个文件里,之后我们再用gdb来分析问题。
 
 
 
参考网址:
 
 
 
http://www.cnblogs.com/wwang/archive/2010/11/14/1876735.html
 
 
 
http://blog.csdn.net/zhandoushi1982/archive/2009/10/21/4711084.aspx
 
 
 
http://dev.firnow.com/course/6_system/linux/Linuxjs/2008717/133299.html
你可能感兴趣的文章
Selenium-Switch与SelectApi接口详解
查看>>
Selenium-Css Selector使用方法
查看>>
Linux常用统计命令之wc
查看>>
测试必会之 Linux 三剑客之 sed
查看>>
Socket请求XML客户端程序
查看>>
Java中数字转大写货币(支持到千亿)
查看>>
Java.nio
查看>>
函数模版类模版和偏特化泛化的总结
查看>>
VMware Workstation Pro虚拟机不可用解决方法
查看>>
最简单的使用redis自带程序实现c程序远程访问redis服务
查看>>
redis学习总结-- 内部数据 字符串 链表 字典 跳跃表
查看>>
iOS 对象序列化与反序列化
查看>>
iOS 序列化与反序列化(runtime) 01
查看>>
iOS AFN 3.0版本前后区别 01
查看>>
iOS ASI和AFN有什么区别
查看>>
iOS QQ侧滑菜单(高仿)
查看>>
iOS 扫一扫功能开发
查看>>
iOS app之间的跳转以及传参数
查看>>
iOS __block和__weak的区别
查看>>
Android(三)数据存储之XML解析技术
查看>>