android 核心出現kernel panic的分析

來源:互聯網
上載者:User

1 何謂OOPS
Oops是美國人比較常有的口語。就是有點意外,吃驚,或突然的意思。“Oops”並不是很嚴重,正如在Britney Spears的 “Oops I Did It Again”那首歌的歌詞中,也是一種輕描淡寫,有時含有抱歉的意思。

http://v.youku.com/v_show/id_XMTM0ODgxMDYw.html

對於Linux核心來說,Oops就意外著核心出了異常,此時會將產生異常時CPU的狀態,出錯的指令地址、資料地址及其他寄存器,函數調用的順序甚至是棧裡面的內容都列印出來,然後根據異常的嚴重程度來決定下一步的操作:殺死導致異常的進程或者掛起系統。

最典型的異常是在核心態引用了一個非法地址,通常是未初始化的野指標Null,這將導致頁表異常,最終引發Oops。

Linux系統足夠健壯,能夠正常的反應各種異常。異常通常導致當前進程的死亡,而系統依然能夠繼續運轉,但是這種運轉都處在一種不穩定的狀態,隨時可能出問題。對於中斷內容相關的異常及系統關鍵資源的破壞,通常會導致核心掛起,不再響應任何事件。

2 核心的異常層級
2.1 Bug
Bug是指那些不符合核心的正常設計,但核心能夠檢測出來並且對系統運行不會產生影響的問題,比如在原子上下文中休眠。如:
BUG: scheduling while 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

2.2 Oops
程式在核心態時,進入一種異常情況,比如引用非法指標導致的資料異常,數組越界導致的取指異常,此時異常處理機制能夠捕獲此異常,並將系統關鍵資訊列印到串口上,正常情況下Oops訊息會被記錄到系統日誌中去。

Oops發生時,進程處在核心態,很可能正在訪問系統關鍵資源,並且擷取了一些鎖,當進程由於Oops異常退出時,無法釋放已經擷取的資源,導致其他需要擷取此資源的進程掛起,對系統的正常運行造成影響。通常這種情況,系統處在不穩定的狀態,很可能崩潰。

2.3 Panic
當Oops發生在中斷上下文中或者在進程0和1中,系統將徹底掛起,因為中斷服務程式異常後,將無法恢複,這種情況即稱為核心panic。另外當系統設定了panic標誌時,無論Oops發生在中斷上下文還是進程上下文,都將導致核心Panic。由於在中斷複位程式中panic後,系統將不再進行調度,Syslogd將不會再運行,因此這種情況下,Oops的訊息僅僅列印到串口上,不會被記錄在系統日誌中。

Kernelpanic調試舉例:

  [ 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 dereference at 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 : [<c07446c0>]    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  Mode SVC_32 ISA ARM  Segment kernel
[  245.910772] Control: 10c5787d  Table: 5a47406a  DAC: 00000015
[  245.916576] 
[  245.916579] PC: 0xc0744640:
[  245.920751] 4640  e3310000 1afffffa f57ff04f e320f004 e5973004e2433001 e5873004 e5973000
[  245.928910] 4660  ea000042 e59f0190 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 to handle kernel NULL pointerdereference at virtual address 00000008時,就知道這個函數應用了一個非法地址,在linux中, 將最高的1G位元組(從虛擬位址0xC0000000到0xFFFFFFFF),供核心使用,稱為“核心空間”。而將較低的3G位元組(從虛擬位址 0x00000000到0xBFFFFFFF),供各個進程使用,稱為“使用者空間),現在核心非法使用了使用者空間的地址故存在問題。

     關於kernel panic一般很難複現,於是我計劃在核心中自己用代碼去類比這個現象。 

static inlinevoid hci_proto_connect_cfm(struct hci_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(struct hci_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也就分析結束了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.