oops是英语口语"糟糕"的意思,当LINUX 内核发生严重错误时,比如内存段错误时,将会提示一大段信息。
Oops提示信息相当多,包括出问题时的,各个常用寄存器的值,调用的堆栈,以及出错的可能原因。
oops 的格式
内核的文档里的详细的Oops的说明,的名字是
Documentation/oops-tracing.txt
http://www.mjmwired.net/kernel/Documentation/oops-tracing.txt
oops第一段出错是内存page地址,
例如提示,
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c7510000
往往表示碰到空指针了。
第二段出错时是寄存器的快照,不同CPU显示不同情况;
其中基本上所有CPU都会有的PC寄存器(Program counter register),它保存最后出问题的地址。
LR保存着函数返回地址。这里就比较容易看出是谁出问题
例如,
<8>PC is at memcpy+0x48/0x330
<8>LR is at s3c_fimc_v4l2_enum_fmt_vid_cap+0x44/0x4c
这里可以看出,最后出问题代码在 memcpy里,而它是被s3c_fimc_v4l2_enum_fmt_vid_cap调用。结合前面的原因,可以知道应该是在memcpy里碰到空指针。
函数名后面的两个数字,第一个是调用偏移量,第二个是函数总尺寸。
最重要是第三段,即可出错的调用堆栈(Call Trace).从这里即可推算出错的所在函数,
oops消息产生机制
oops(也称 panic),称程序运行崩溃,程序崩溃后会产生oops消息。应用程序或内核线程的崩溃都会产生oops消息,通常发生oops时,系统不会发生死机,而在终端或日志中打印oops信息。
当使用NULL指针或不正确的指针值时,通常会引发一个 oops 消息,这是因为当引用一个非法指针时,页面映射机制无法将虚拟地址映像到物理地址,处理器就会向操作系统发出一个"页面失效"的信号。内核无法"换页"到并不存在的地址上,系统就会产生一个"oops"。
oops 显示发生错误时处理器的状态,包括 CPU 寄存器的内容、页描述符表的位置,以及其一些难理解的信息。这些消息由失效处理函数(arch/*/kernel/traps.c)中的printk 语句产生。较为重要的信息就是指令指针(EIP),即出错指令的地址。
由于很难从十六进制数值中看出含义,可使用符号解析工具klogd。klogd 守护进程能在 oops 消息到达记录文件之前对它们解码。klogd在缺省情况下运行并进行符号解码。
Linux内核提供了调用,允许程序决定装载哪些模块和它们在内存中位置。通过这些系统调用,klogd守护进程生成一张符号表用于调试发生在可装载模块中的保护错误。内核模块的装载或者卸载都会自动向klogd发送信号,klogd可将内核模块符号的地址动态翻译为符号字符串。
下面来搞2个错误程序,看一下oops消息的基本情况;
先看test1.c;
#include <stdlib.h>
#include <string.h>
int main()
{
char *p = (char *) malloc(100);
strcpy(p, "hello");
free(p); // p 所指的内存被释放,但是p所指的地址仍然不变
if(p != NULL) // 没有起到防错作用
{
strcpy(p, "world"); // 出错
}
getchar();
return 0;
}
此程序应该出错,因为p已释放,然后执行了strcpy(p, "xxxx"); 但是没有产生dump;
再看test2.c;
#include <stdlib.h>
int main()
{
*(int *)0 = 0;
getchar();
return 0;
}
test2.exe崩溃了,产生了dump;
在exe目录下生成了一个dump文件;格式和内容如前所述;有时间再细看;