linux 串口中断_Linux 内核的异常类型

Linux 内核的异常级bug /oops /panic 及举例

 内核的异常级别

Bug

Bug是指那些不符合内核的正常设计,但内核能够检测出来并且对系统运行不会产生影响的问题,比如在原子上下文中休眠。如:

BUG: schedulingwhile atomic: insmod/826/0x00000002

Call Trace:

[ef12f700][c00081e0] show_stack+0x3c/0x194 (unreliable)

[ef12f730][c0019b2c] __schedule_bug+0x64/0x78

[ef12f750][c0350f50] schedule+0x324/0x34c

[ef12f7a0][c03515c0] schedule_timeout+0x68/0xe4

[ef12f7e0][c027938c] fsl_elbc_run_command+0x138/0x1c0

[ef12f820][c0275820] nand_do_read_ops+0x130/0x3dc

[ef12f880][c0275ebc] nand_read+0xac/0xe0

[ef12f8b0][c0262d98] part_read+0x5c/0xe4

[ef12f8c0][c017bcac] jffs2_flash_read+0x68/0x254

[ef12f8f0][c0170550] jffs2_read_dnode+0x60/0x304

[ef12f940][c017088c] jffs2_read_inode_range+0x98/0x180

[ef12f970][c016e610] jffs2_do_readpage_nolock+0x94/0x1ac

[ef12f990][c016ee04] jffs2_write_begin+0x2b0/0x330

[ef12fa10][c005144c] generic_file_buffered_write+0x11c/0x8d0

[ef12fab0][c0051e48] __generic_file_aio_write_nolock+0x248/0x500

[ef12fb20][c0052168] generic_file_aio_write+0x68/0x10c

[ef12fb50][c007ca80] do_sync_write+0xc4/0x138

[ef12fc10][f107c0dc] oops_log+0xdc/0x1e8 [oopslog]

[ef12fe70][f3087058] oops_log_init+0x58/0xa0 [oopslog]

[ef12fe80][c00477bc] sys_init_module+0x130/0x17dc

[ef12ff40][c00104b0] ret_from_syscall+0x0/0x38

--- Exception: c01 at 0xff29658

   LR = 0x10031300

Oops

程序在内核态时,进入一种异常情况,比如引用非法指针导致的数据异常,数组越界导致的取指异常,此时异常处理机制能够捕获此异常,并将系统关键信息打印到串口上,正常情况下Oops消息会被记录到系统日志中去。

Oops发生时,进程处在内核态,很可能正在访问系统关键资源,并且获取了一些锁,当进程由于Oops异常退出时,无法释放已经获取的资源,导致其他需要获取此资源的进程挂起,对系统的正常运行造成影响。通常这种情况,系统处在不稳定的状态,很可能崩溃。

对于Linux内核来说,Oops就意外着内核出了异常,此时会将产生异常时CPU的状态,出错的指令地址、数据地址及其他寄存器,函数调用的顺序甚至是栈里面的内容都打印出来,然后根据异常的严重程度来决定下一步的操作:杀死导致异常的进程或者挂起系统。

最典型的异常是在内核态引用了一个非法地址,通常是未初始化的野指针Null,这将导致页表异常,最终引发Oops。

Panic

当Oops发生在中断上下文中或者在进程0和1中,系统将彻底挂起,因为中断服务程序异常后,将无法恢复,这种情况即称为内核panic。另外当系统设置了panic标志时,无论Oops发生在中断上下文还是进程上下文,都将导致内核Panic。由于在中断复位程序中panic后,系统将不再进行调度,Syslogd将不会再运行,因此这种情况下,Oops的消息仅仅打印到串口上,不会被记录在系统日志中。

Kernel panic调试举例:

  [ 242.788019]bluesleep_outgoing_data: tx was sleeping

[  244.012224] ******host_wake is 1

[  245.234647]Disable_key_during_touch=0 

[  245.237802]huqiao___button->code=139,state =1 

[  245.414640]Disable_key_during_touch=0 

[  245.417542]huqiao___button->code=139,state =0 

[  245.821424] ******host_wake is 0

[  245.823708] bluesleep_hostwake_isr:[I]waking up...

[  245.823713] 

[  245.830155] bluesleep_hostwake_task:bluesleep_hostwake_task is called

[  245.838356] Unable to handle kernel NULL pointer dereferenceat virtualaddress 00000008

[  245.845678] pgd = c0004000

[  245.848188] [00000008] *pgd=00000000

[  245.851751] Internal error: Oops: 5 [#1] PREEMPT SMP ARM

[  245.857122] Modules linked in:

[  245.860080] CPU: 0   Tainted: G        W    (3.4.0-perf-svn874#1)

[  245.866444] PC is at sco_connect_cfm+0x380/0x4e8

[  245.871106] LR is at 0xd880

[  245.873800] pc : []   lr :[<0000d880>]    psr: 40000013

[  245.873805] sp : dbe55e78  ip :00000000  fp : d7d95c00

[  245.885246] r10: d8643998  r9 :d8e5b80d  r8 : d8643830

[  245.890529] r7 : dbe54000  r6 :d9e5b600  r5 : cae27c80 r4 : d8643800

[  245.896968] r3 : 00000008  r2 :00000000  r1 : d7d96016 r0 : 00000000

[  245.903552] Flags: nZcv  IRQs on  FIQs on  ModeSVC_32 ISA ARM  Segment kernel

[  245.910772] Control: 10c5787d Table: 5a47406a  DAC: 00000015

[  245.916576] 

[  245.916579] PC: 0xc0744640:

[  245.920751] 4640  e33100001afffffa f57ff04f e320f004 e5973004e2433001 e5873004 e5973000

[  245.928910] 4660  ea000042e59f0190 e300332a e19030b3 e31300040a000004 e2800fc6 e59f1198

 如上图,当出现kernel panic的时候,会出现上面所示的堆栈信息。

我们可以看到 [ 245.866444] PC is atsco_connect_cfm+0x380/0x4e8,就会知道在sco_connect_cfm函数这边出现问题的。一般来说从LR(链接寄存器)这,我们可以知道上面的哪个函数是被hci_proto_connect_cfm所调用的。

当看到Unable tohandle kernel NULL pointerdereferenceat virtual address00000008时,就知道这个函数应用了一个非法地址,在linux中, 将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间),现在内核非法使用了用户空间的地址故存在问题。

     关于kernelpanic一般很难复现,于是我计划在内核中自己用代码去模拟这个现象。 

static inlinevoid hci_proto_connect_cfm(structhci_conn *conn, __u8 status)

{

register struct hci_proto *hp;

hp = hci_proto[HCI_PROTO_L2CAP];

if (hp && hp->connect_cfm)

hp->connect_cfm(conn, status);

hp = hci_proto[HCI_PROTO_SCO];

if (hp && hp->connect_cfm)

hp->connect_cfm(conn, status);

if (conn->connect_cfm_cb)

conn->connect_cfm_cb(conn, status);

}

当我把函数改变为

static inlinevoid hci_proto_connect_cfm(structhci_conn *conn, __u8 status)

{

register struct hci_proto *hp;

hp = hci_proto[HCI_PROTO_L2CAP];

if (hp && hp->connect_cfm)

hp->connect_cfm(conn, status);

conn =  = NULL - 21; // Simulation this phenomenon,

hp = hci_proto[HCI_PROTO_SCO];

if (hp && hp->connect_cfm)

hp->connect_cfm(conn, status);

if (conn->connect_cfm_cb)

conn->connect_cfm_cb(conn, status);

}

       这个现象就会完全的复现。

     其实根据 hci_conn结构体定义,我们就会知道 hcon->type的地址为00000008,于是我们就会明白,在最初的代码中,在调用sco_connect_cfm的时候,传入的变量conn的地址被改变为NULL- 21;但是在前面跑hp->connect_cfm(conn, status)却没有什么问题,conn的地址传进 hp->connect_cfm(conn, status),也没有什么改变。于是我就开始郁闷了。为什么突然地址变为一个非法的地址?

    后来在网上查了下,才发现可能是硬件的问题,使得某一个地址发生了临时的错误而导致的。于是找到了原因,这个bug也就分析结束了。

 附Linux 内核启动流程图

9495fb023956ac5caf504e2e1b459d70.png

 来自 <https://blog.csdn.net/dddxxxx/article/details/77099057> 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值