Linux内核Crash分析

http://blog.chinaunix.net/uid-20788636-id-4377271.html 

在工作中经常会遇到一些内核crash的情况,本文就是根据内核出现crash后的打印信息,对其进行了分析,使用的内核版本为:Linux2.6.32。

每一个进程的生命周期内,其生命周期的范围为几毫秒到几个月。一般都是和内核有交互,例如用户空间程序使用系统调用进入内核空间。这时使用的不再是用户空间的栈空间,使用对应的内核栈空间。对每一个进程来说,Linux内核都会把两个不同的数据结构紧凑的存放在一个单独为进程分配的存储空间中:一个是内核态的进程堆栈,另一个是紧挨进程描述符的数据结构thread_info,叫线程描述符。内核的堆栈大小一般为8KB,也就是8192个字节,占用两个页。在Linux-2.6.32内核中thread_info.h文件中有对内核堆栈的定义:

#define THREAD_SIZE               8192

         在Linux内核中使用下面的联合结构体表示一个进程的线程描述符和内核栈,在内核中文件include/linux/sched.h。

union thread_union {

         struct thread_info thread_info;

         unsigned long stack[THREAD_SIZE/sizeof(long)];

};      

         该结构是一个联合体,我们在C语言书上看到过关于union的解释,在在C Programming Language 一书中对于联合体是这么描述的:

1) 联合体是一个结构;

2) 它的所有成员相对于基地址的偏移量都为0;

3) 此结构空间要大到足够容纳最"宽"的成员;

4) 其对齐方式要适合其中所有的成员;

         通过上面的描述可知,thread_union结构体的大小为8192个字节。也就是stack数组的大小,类型是unsigned long类型。由于联合体中的成员变量都是占用同一块内存区域,所以,在平时写代码时总有一个概念,对一个联合体的实例只能使用其中一个成员变量,否则会把原先变量给覆盖掉,这句话如果正确的话,必须要有一个前提假设,成员占用的字节数相同,当成员所占的字节数不同时,只会覆盖相应的字节。对于thread_union联合体,我们是可以同时访问这两个成员,只要能够正确获取到两个成员变量的地址。

         在内核中的某一个进程使用了过多的栈空间时,内核栈就会溢出到thread_info部分,这将导致严重的问题(系统重启),例如,递归调用的层次太深;在函数内定义的数据结构太大。

图:进程中thread_info    task_struct和内核栈中的关系

         下面我们看一下thread_info的结构体:

struct thread_info {

         unsigned long           flags;                  /* 底层标志,*/

         int                       preempt_count;       /* 0 => 可抢占, <0 => bug */

         mm_segment_t                addr_limit;       /* 进程地址空间 */

         struct task_struct   *task;                /*当前进程的task_struct指针 */

         struct exec_domain         *exec_domain;         /*执行区间 */

         __u32                          cpu;           /* 当前cpu */

         __u32                          cpu_domain;    /* cpu domain */

         struct cpu_context_save         cpu_context;   /* cpu context */

         __u32                          syscall;     /* syscall number */

         __u8                            used_cp[16];    /* thread used copro */

         unsigned long           tp_value;

         struct crunch_state         crunchstate;

         union fp_state          fpstate __attribute__((aligned(8)));

         union vfp_state                 vfpstate;

#ifdef CONFIG_ARM_THUMBEE

         unsigned long           thumbee_state;       /* ThumbEE Handler Base register */

#endif

         struct restart_block         restart_block; /*用于实现信号机制*/

};

PS:(1)flag 用于保存各种特定的进程标志,最重要的两个是:TIF_SIGPENDING,如果进程有待处理的信号就置位,TIF_NEED_RESCHED表示进程应该需要调度器选择另一个进程替换本进程执行。

         结合上面的知识,看下当内核打印堆栈信息时,都打印了上面信息。下面的打印信息是工作中遇到的一种情况,打印了内核的堆栈信息,PC指针在dev_get_by_flags中,不能访问的内核虚地址为45685516,内核中一般可访问的地址都是以0xCXXXXXXX开头的地址。

Unable to handle kernel paging request at virtual address 45685516

pgd = c65a4000

[45685516] *pgd=00000000

Internal error: Oops: 1 [#1]

last sysfs file: /sys/devices/form/tpm/cfg_l3/l3_rule_add

Modules linked in: splic mmp(P)

CPU: 0    Tainted: P            (2.6.32.11 #42)

PC is at dev_get_by_flags+0xfc/0x140

LR is at dev_get_by_flags+0xe8/0x140

pc : [<c06bee24>]    lr : [<c06bee10>]    psr: 20000013

sp : c07e9c28  ip : 00000000  fp : c07e9c64

r10: c6bcc560  r9 : c646a220  r8 : c66a0000

r7 : c6a00000  r6 : c0204e56  r5 : 30687461  r4 : 45685516

r3 : 00000000  r2 : 00000010  r1 : c0204e56  r0 : ffffffff

Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel

Control: 0005397f  Table: 065a4000  DAC: 00000017

Process swapper (pid: 0, stack limit = 0xc07e8270)

Stack: (0xc07e9c28 to 0xc07ea000)

9c20:                   c0204e56 c6a00000 45685516 c69ffff0 c69ffff0 c69ffff0

9c40: c6a00000 30687461 c66a0000 c6a00000 00000007 c64b210c c07e9d24 c07e9c68

9c60: c071f764 c06bed38 c66a0000 c66a0000 c6a00000 c6a00000 c66a0000 c6a00000

9c80: c07e9cfc c07e9c90 c03350d4 c0334b2c 00000034 00000006 00000100 c64b2104

9ca0: 0000c4fb c0243ece c66a0000 c0beed04 c033436c c646a220 c07e9cf4 00000000

9cc0: c66a0000 00000003 c0bee8e8 c0beed04 c07e9d24 c07e9ce0 c06e4f5c 00004c68

9ce0: 00000000 faa9fea9 faa9fea9 00000000 00000000 c6bcc560 c0335138 c646a220

9d00: c66a0000 c64b2104 c085ffbc c66a0000 c0bee8e8 00000000 c07e9d54 c07e9d28

9d20: c071f9a0 c071ebc0 00000000 c071ebb0 80000000 00000007 c67fb460 c646a220

9d40: c0bee8c8 00000608 c07e9d94 c07e9d58 c002a100 c071f84c c0029bb8 80000000

9d60: c07e9d84 c0beee0c c0335138 c66a0000 c646a220 00000000 c4959800 c4959800

9d80: c67fb460 00000000 c07e9dc4 c07e9d98 c078f0f4 c0029bc8 00000000 c0029bb8

9da0: 80000000 c07e9dbc c6b8d340 c66a0520 00000000 c646a220 c07e9dec c07e9dc8

9dc0: c078f450 c078effc 00000000 c67fb460 c6b8d340 00000000 c67fb460 c64b20f2

9de0: c07e9e24 c07e9df0 c078fb60 c078f130 00000000 c078f120 80000000 c0029a94

9e00: 00000806 c6b8d340 c0bee818 00000001 00000000 c4959800 c07e9e64 c07e9e28

9e20: c002a030 c078f804 c64b2070 00000000 c64b2078 ffc45000 c64b20c2 c085c2dc

9e40: 00000000 c085c2c0 00000000 c0817398 00086c2e c085c2c4 c07e9e9c c07e9e68

9e60: c06c2684 c0029bc8 00000001 00000040 00000000 c085c2dc c085c2c0 00000001

9e80: 0000012c 00000040 c085c2d0 c0bee818 c07e9ed4 c07e9ea0 c00284e0 c06c2608

9ea0: bf00da5c 00086c30 00000000 00000001 c097e7d4 c07e8000 00000100 c08162d8

9ec0: 00000002 c097e7a0 c07e9f14 c07e9ed8 c00283d0 c0028478 56251311 00023c88

9ee0: c07e9f0c 00000003 c08187ac 00000018 00000000 01000000 c07ebc70 00023cbc

9f00: 56251311 00023c88 c07e9f24 c07e9f18 c03391e8 c0028348 c07e9f3c c07e9f28

9f20: c0028070 c03391b0 ffffffff 0000001f c07e9f94 c07e9f40 c002d4d0 c0028010

9f40: 00000000 00000001 c07e9f88 60000013 c07e8000 c07ebc78 c0868784 c07ebc70

9f60: 00023cbc 56251311 00023c88 c07e9f94 c07e9f98 c07e9f88 c025c3e4 c025c3f4

9f80: 60000013 ffffffff c07e9fb4 c07e9f98 c025c578 c025c3cc 00000000 c0981204

9fa0: c0025ca0 c0d01140 c07e9fc4 c07e9fb8 c0032094 c025c528 c07e9ff4 c07e9fc8

9fc0: c0008918 c0032048 c0008388 00000000 00000000 c0025ca0 00000000 00053975

9fe0: c0868834 c00260a4 00000000 c07e9ff8 00008034 c0008708 00000000 00000000

Backtrace:

[<c06bed28>] (dev_get_by_flags+0x0/0x140) from [<c071f764>] (arp_process+0xbb4/0xc74)

 r7:c64b210c r6:00000007 r5:c6a00000 r4:c66a0000

         (1)首先,看看这段堆栈信息是在内核中那个文件中打印出来的,在fault.c文件中,__do_kernel_fault函数,在上面的打印中Unable to handle kernel paging request at virtual address 45685516该地址是内核空间不可访问的地址。

static void

__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,

                     struct pt_regs *regs)

{

         /*

          * Are we prepared to handle this kernel fault?

          */

         if (fixup_exception(regs))

                   return;

         /*

          * No handler, we'll have to terminate things with extreme prejudice.

          */

         bust_spinlocks(1);

         printk(KERN_ALERT

                   "Unable to handle kernel %s at virtual address %08lx\n",

                   (addr < PAGE_SIZE) ? "NULL pointer dereference" :

                   "paging request", addr);

         show_pte(mm, addr);

         die("Oops", regs, fsr);

         bust_spinlocks(0);

         do_exit(SIGKILL);

}

(2) 对于下面的两个信息,在函数show_pte中进行了打印,下面的打印涉及到了页全局目录,页表的知识,暂时先不分析,后续补上。

pgd = c65a4000

[45685516] *pgd=00000000

void show_pte(struct mm_struct *mm, unsigned long addr)

{

         pgd_t *pgd;

 

         if (!mm)

                   mm = &init_mm;

 

         printk(KERN_ALERT "pgd = %p\n", mm->pgd);

         pgd = pgd_offset(mm, addr);

         printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd));

……………………

}

(3) die函数中调用

         在die函数中取得thread_info结构体的地址。

          struct thread_info *thread = current_thread_info();

static inline struct thread_info *current_thread_info(void)

{

          register unsigned long sp asm ("sp");

          return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));

}

Sp: 0xc07e9c28    通过current_thread_info得到 thread_info的地址

(0xc07e9c28 & 0xffffe000) = 0xC07E8000(thread_info的地址,也就是栈底的地址)

(4)下面的打印信息在__die函数中打印

Internal error: Oops: 1 [#1]

last sysfs file: /sys/devices/form/tpm/cfg_l2/l2_rule_add

Modules linked in: splic mmp(P)

CPU: 0    Tainted: P            (2.6.32.11 #42)

PC is at dev_get_by_flags+0xfc/0x140

LR is at dev_get_by_flags+0xe8/0x140

pc : [<c06bee24>]    lr : [<c06bee10>]    psr: 20000013

sp : c07e9c28  ip : 00000000  fp : c07e9c64

r10: c6bcc560  r9 : c646a220  r8 : c66a0000

r7 : c6a00000  r6 : c0204e56  r5 : 30687461  r4 : 30687461

r3 : 00000000  r2 : 00000010  r1 : c0204e56  r0 : ffffffff

Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel

Control: 0005397f  Table: 065a4000  DAC: 00000017

Process swapper (pid: 0, stack limit = 0xc07e8270)

Stack: (0xc07e9c28 to 0xc07ea000)

         函数的调用关系:die("Oops", regs, fsr);---à     __die(str, err, thread, regs);

下面是__die函数的定义:

static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs)

{

          struct task_struct *tsk = thread->task;

          static int die_counter;

/*Internal error: Oops: 1 [#1]*/

          printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",

                 str, err, ++die_counter);

/*last sysfs file: /sys/devices/form/tpm/cfg_l2/l2_rule_add*/

          sysfs_printk_last_file();

/*内核中加载的模块信息Modules linked in: splic mmp(P) */

          print_modules();

/*打印寄存器信息*/

          __show_regs(regs);

/*Process swapper (pid: 0, stack limit = 0xc07e8270) tsk->comm  task_struct结构体中的comm表示的是除去路径后的可执行文件名称,这里的swapperidle进程,进程号为0,创建内核进程init;其中stack limit = 0xc07e8270  指向thread_info的结束地址。*/

          printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n",

                   TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);

/* dump_mem 函数打印从栈顶到当前sp之间的内容*/

          if (!user_mode(regs) || in_interrupt()) {

                   dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp,

                             THREAD_SIZE + (unsigned long)task_stack_page(tsk));

                   dump_backtrace(regs, tsk);

                   dump_instr(KERN_EMERG, regs);

          }

}

         在上面的函数中,主要使用了thread_info,task_struct,sp之间的指向关系。task_struct结构体的成员stack是栈底,也是对应thread_info结构体的地址。堆栈数据是从

栈底+8K的地方开始向下存的。SP指向的是当前的栈顶。(unsigned long)task_stack_page(tsk),

#define task_stack_page(task)        ((task)->stack) ,该宏根据task_struct得到栈底,也就是thread_info地址。

#define task_thread_info(task)       ((struct thread_info *)(task)->stack),该宏根据task_struct得到thread_info指针。

(5)dump_backtrace函数

         该函数用于打印函数的调用关系。Fp为帧指针,用于追溯程序的方式,方向跟踪调用函数。该函数主要是fp进行检查,看看能否进行backtrace,如果可以就调用汇编的c_backtrace,在arch/arm/lib/backtrace.S函数中。

static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)

{

         unsigned int fp, mode;

         int ok = 1;

 

         printk("Backtrace: ");

 

         if (!tsk)

                   tsk = current;

 

         if (regs) {

                   fp = regs->ARM_fp;

                   mode = processor_mode(regs);

         } else if (tsk != current) {

                   fp = thread_saved_fp(tsk);

                   mode = 0x10;

         } else {

                   asm("mov %0, fp" : "=r" (fp) : : "cc");

                   mode = 0x10;

         }

 

         if (!fp) {

                   printk("no frame pointer");

                   ok = 0;

         } else if (verify_stack(fp)) {

                   printk("invalid frame pointer 0x%08x", fp);

                   ok = 0;

         } else if (fp < (unsigned long)end_of_stack(tsk))

                   printk("frame pointer underflow");

         printk("\n");

 

         if (ok)

                   c_backtrace(fp, mode);

}

(6)dump_instr

根据PC指针和指令mode, 打印出当前执行的指令码

Code: 0a000008 e5944000 e2545000 0a000005 (e4153010)

内核中函数的调用关系

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432935.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java用beaninfo_JavaBeanInfo 和 Spring 之间的关系

Java Beans在这一章章节中笔者将和各位一起探讨关于 Java Beans 相关的内容。本章主要围绕 java.beans 这个包路径下的代码进行一些说明。在 Spring 中我们可以看到 BeanInfoFactory 该接口可以用来获取 Class 对应的 BeanInfo 对象&#xff0c;在 CachedIntrospectionResults …

selenium ruby和java_Selenium 2之Ruby版——安装篇

自从知道了Selenium的存在后&#xff0c;就一直都想&#xff0c;若要学习自动化&#xff0c;就要学习像Selenium这种比较有潜力的。Selenium有针对各种语言(java, C#, Python, Ruby, Perl)的版本&#xff0c;在此选择Ruby为学习方向&#xff0c;一来可以借此学习下Ruby&#xf…

基于visual Studio2013解决面试题之0702输出数字

&#xfeff;&#xfeff;&#xfeff;题目解决代码及点评/*输入数字 n&#xff0c;按顺序输出从 1 最大的 n 位 10 进制数。比如输入 3&#xff0c;则输出 1、2、3一直到最大的 3 位数即 999。 */#include <iostream> using namespace std;//在不考虑大数的情况下&#…

冠榕智能灯光控制协议分析(controller-node)

1. 在Z-WAVE PC Controller软件选择已配对的智能开关。 从上图中可以看到&#xff0c;我们的智能开关的node id是11&#xff0c;即0x0B。 2. 向智能开关发送灯光的开闭数据。 CommandClasses选择COMMAND_CLASS_BASIC CommandName选择BASIC_SET Value为00时关闭灯光&#xff0…

java中手动装入新类到类装饰器_关于java:抽象装饰器类中的功能而不是装饰器...

我目前正在阅读《Head First Design Patterns》一书&#xff0c;在"Decorator"一章中有以下示例&#xff1a;在书中&#xff0c;conditionmentDecorator类被描述为一个abstract decorator。下面是代码示例&#xff1a;public abstract class CondimentDecorator exte…

跨浏览器开发工作小结

本篇小结是在2011年时候总结的&#xff0c;当时做一个产品的跨浏览器兼容工作&#xff0c;由于产品开发的时间比较早&#xff0c;最开始只能在IE下面(IE 8、IE 9还有点点问题)使用&#xff0c;做跨浏览器兼容工作的时候&#xff0c;主要是适配IE 6--IE 9、Safari、FireFox、Chr…

冠榕智能灯光控制协议分析(node-controller)

1. 在Z-WAVE PC Controller软件选择已配对的智能开关。 从上图中可以看到&#xff0c;我们的智能开关的node id是11&#xff0c;即0x0B。 2. 按下智能开关&#xff0c;用串口工具可以看到以下信息。 01 0D 00 04 00 0B 07 60 0D 01 01 00 03 FF 6B 01 0D 00 04 00 0B 07 60…

冠榕智能灯光控制协议分析(controller-node) 2

z-wave第一篇&#xff0c;我们用COMMAND_CLASS_BASIC - BASIC_SET控制智能开关。但是智能开关上有两个执行器&#xff0c;我们只能控制其中一路&#xff0c;那么我们如何控制另一路的开关的。在z-wave第二篇&#xff0c;我们分析了智能开关两个按键发送的消息&#xff0c;发现&…

基于visual Studio2013解决面试题之0902内存拷贝

&#xfeff;&#xfeff;&#xfeff;题目解决代码及点评/*用 C 语言实现函数 void * memmove(void *dest,const void *src,size_t n)memmove 函数的功能是拷贝 src 所指的内存内容前 n 个字节到 dest 所指的地址上。 简单循环拷贝即可&#xff0c;但是这道题&#xff0c;要深…

冠榕智能灯光控制协议分析(controller init)

上面几篇已经详细介绍了z-wave协议的分析方法&#xff0c;这一章&#xff0c;我们分析z-wave pc controller初始化时的通信信息。我们只将关键信息列出&#xff0c;然后直接将分析出来的串口数据列出。 1. 得到z-wave版本 01 03 00 15 E9 06 01 10 01 15 5A 2D 57 61 76 65…

jmeter找不到java_Windows下Jmeter安装出现Not able to find Java executable or version问题解决方案...

最近在做一个开放接口平台性能测试 , 指标是最少达到1000/s的并发 , 接口鉴权 百万级的表 在1s内完成..在众多压测工具中 ,,选择了Apache的jmeter ,于官网下载了最新版本http://jmeter.apache.org/download_jmeter.cgi (jmeter下载地址)由于jmeter运行是基于java的,所以需要…

ZDB5304烧写方法

1&#xff0e; 跳线和5304的位置如下图 2. 打开z-wave programmer软件&#xff0c;设置如下图&#xff0c;注意烧写接口为uart&#xff0c;烧写的时候会提示的。选yes是uart&#xff0c;选no是spi。 烧写过程中会提示按下reset或释放reset按键。照做即可。 烧完后&#xff0c…

基于Z-Wave无线技术的指纹锁系统设计

http://www.chinaaet.com/article/218940 摘 要&#xff1a; 结合新兴的低功耗的Z-Wave短距无线通信技术&#xff0c;设计一种应用于酒店的智能指纹锁无线管理与控制系统。该系统的门锁硬件电路包括主控制器S3C2440、指纹采集模块、电机驱动模块及ZM3102无线模块&#xff0c;…

Hibernate一对一关联------主键关联(亲测成功)

1、创建两个实体&#xff08;Company.java和Login.java&#xff09;代码如下&#xff1a; 1 package wck.stu.vo.onetoonein;2 3 public class Company {4 private String id "";5 6 private String companyName "";7 8 private Str…

INTEL和AMD两大巨头的前身

仙童半导体公司&#xff0c;曾经是世界上最大、最富创新精神和最令人振奋的半导体生产企业&#xff0c;为硅谷的成长奠定了坚实的基础。更重要的是&#xff0c;这家公司还为硅谷孕育了成千上万的技术人才和管理人才&#xff0c;它不愧是电子、电脑业界的“西点军校”&#xff0…

servlet加载资源两种方式-内外(初始化参数).properties文件

在servlet中加载资源很方便&#xff0c;通过servletContext&#xff0c;这个是web服务器加载完web项目返回的参数之一&#xff0c;还有servletConfig&#xff0c;得到web项目一些配置信息&#xff0c;request和response,等等&#xff0c;我们现在用的这个servletContext也叫做C…

java 登录编程_Java编程通过session访问需要登录的页面

使用Java访问一般网页&#xff0c;进行数据抓取等比较简单&#xff0c;直接用URL和URLConnection连接所需要的网站地址即可&#xff0c;然后对返回的html源码进行处理分析&#xff0c;获取感兴趣的内容。不过如果是需要登录后才能访问的网页就不能直接传入网站url进行连接了&am…

ZDB5202烧成控制器方法

1&#xff0e;跳线和ZDB5202的安装位置如下图 黄色开关设置为off状态&#xff0c;就是靠近班子里面的一方。 2&#xff0e;打开z-wave programmer软件&#xff0c;设置如下图&#xff0c;注意烧写接口为spi&#xff0c;烧写的时候会提示的。选yes是uart&#xff0c;选no是spi。…

controller配对与接触配对

1&#xff0e; 当点击添加按钮后 以上数据&#xff0c;都是z-wave主控制器与pc controller软件的交换。 2&#xff0e; 当连续点击从结点的配对按钮后。 3&#xff0e; 当点击remove按钮后 4&#xff0e; 当连续点击从结点接触配对按钮后

php可以调用windowsapi吗_2.如何调用WindowsApi

2.如何调用WindowsApi在上一篇章节中 很不专业的介绍了一下WindowsApi 如果你本身就知道 那你也压根不用看 如果你本身就不知道 就算我说的再多 估计你也觉得是多余 所以还是来点实际的首先 在C#中想要调用那些Api(当然不一定是Windows提供的Api) 你得添加一个引用&#xff1a;…