linux Oops和Panic關係 .

常在河邊走,哪能不溼鞋。用Linux,總有死機的時候,如果運氣好,會看到一些所謂”Oops”信息(在屏幕上或系統日誌中),比如:

Unable to handle kernel paging request at virtual address f899b670
printing eip:
c01de48c
*pde = 00737067
Oops: 0002 [#1]
Modules linked in: bluesmoke_e752x bluesmoke_mc md5 ipv6 parport_pc lp parport nls_cp936 vfat fat dm_mod button battery asus_acpi ac joydev yenta_socket pcmcia_core uhci_hcd ehci_hcd snd_intel8x0 snd_ac97_codec snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_page_alloc snd_mpu401_uart snd_rawmidi snd_seq_device snd soundcore ipw2200 ieee80211 ieee80211_crypt sk98lin ext3 jbd
CPU: 0
EIP: 0060:[] Not tainted VLI
EFLAGS: 00210286 (2.6.9-11.21AXKProbes)
EIP is at kobject_add+0×83/0xd7
eax: c038db78 ebx: c038db04 ecx: f899b670 edx: f8a4a630
esi: c038db4c edi: f8a4a614 ebp: c038db80 esp: d7568f2c
ds: 007b es: 007b ss: 0068
Process modprobe (pid: 8227, threadinfo=d7568000 task=f4ea99b0)
Stack: f8a4a614 ffffffea f8a4a5e4 00000000 c01de4f9 f8a4a614 c038db00 c024a1d4
f8a4a5c0 f8a4a5e4 f8a4a5f4 d7568000 c024a661 1d244b3c 00000000 0000000a
c032421b 00000000 00000000 00000015 00000014 00000016 f89ddb34 f8a4a5c0
Call Trace:
[] kobject_register+0×19/0×39
[] bus_add_driver+0×36/0×97
[] driver_register+0×82/0×89
[] pci_register_driver+0×85/0xa1
[] init_module+0xa/0×14 [bluesmoke_e752x]
[] sys_init_module+0x1ec/0×323
[] syscall_call+0×7/0xb
Code: 85 d2 0f 85 06 04 00 00 85 ed 75 0d 8b 47 28 83 c0 10 e8 82 01 00 00 89 c5 8b 47 28 8d 57 1c 83 c0 08 89 47 1c 8b 48 04 89 50 04 <89> 11 89 4a 04 8b 47 28 8b 18 8d 4b 48 89 c8 ba ff ff 00 00 0f

Oops可以看成是內核級的Segmentation Fault。應用程序如果進行了非法內存訪問或執行了非法指令,會得到Segfault信號,一般的行爲是coredump,應用程序也可以自己截獲Segfault信號,自行處理。如果內核自己犯了這樣的錯誤,則會打出Oops信息。

有不少文章說明如何理解這些Oops (http://pczou.blogchina.com/545558.html),這裏只想解釋一下它所產生的過程(以2.6系列內核爲例):

首先是處理硬件發出的內存訪問異常(fault),有些異常是無辜的(比如demand-paging),而有些則是內核的錯誤所致。

1. do_page_fault() arch/i386/mm/fault.c

如果是內核進行了非法訪問,do_page_fault()會先打出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),會殺死當前進程。然後繼續運行,好像什麼事情都沒有發生一樣。不過,這樣的好事不經常發生,發生了也不會太持久。

2. do_page_fault() -> die() arch/i386/kernel/traps.c

die() 首先打出一行:

Oops: 0002 [#1]

其中0002代表錯誤碼 (讀錯誤、發生在內核空間),#1代表Oops發生次數。

* error_code:
*       bit 0 == 0 means no page found, 1 means protection fault
*       bit 1 == 0 means read, 1 means write
*       bit 2 == 0 means kernel, 1 means user-mode


然後,調用 show_registers(regs) 輸出寄存器、當前進程、堆棧、指令代碼等信息:

Modules linked in: bluesmoke_e752x bluesmoke_mc md5 ipv6 parport_pc lp parport nls_cp936 vfat fat dm_mod button battery asus_acpi ac joydev yenta_socket pcmcia_core uhci_hcd ehci_hcd snd_intel8x0 snd_ac97_codec snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_page_alloc snd_mpu401_uart snd_rawmidi snd_seq_device snd soundcore ipw2200 ieee80211 ieee80211_crypt sk98lin ext3 jbd
CPU: 0
EIP: 0060:[] Not tainted VLI
EFLAGS: 00210286 (2.6.9-11.21AXKProbes)
EIP is at kobject_add+0×83/0xd7
eax: c038db78 ebx: c038db04 ecx: f899b670 edx: f8a4a630
esi: c038db4c edi: f8a4a614 ebp: c038db80 esp: d7568f2c
ds: 007b es: 007b ss: 0068
Process modprobe (pid: 8227, threadinfo=d7568000 task=f4ea99b0)
Stack: f8a4a614 ffffffea f8a4a5e4 00000000 c01de4f9 f8a4a614 c038db00 c024a1d4
f8a4a5c0 f8a4a5e4 f8a4a5f4 d7568000 c024a661 1d244b3c 00000000 0000000a
c032421b 00000000 00000000 00000015 00000014 00000016 f89ddb34 f8a4a5c0
Call Trace:
[] kobject_register+0×19/0×39
[] bus_add_driver+0×36/0×97
[] driver_register+0×82/0×89
[] pci_register_driver+0×85/0xa1
[] init_module+0xa/0×14 [bluesmoke_e752x]
[] sys_init_module+0x1ec/0×323
[] syscall_call+0×7/0xb
Code: 85 d2 0f 85 06 04 00 00 85 ed 75 0d 8b 47 28 83 c0 10 e8 82 01 00 00 89 c5 8b 47 28 8d 57 1c 83 c0 08 89 47 1c 8b 48 04 89 50 04 <89> 11 89 4a 04 8b 47 28 8b 18 8d 4b 48 89 c8 ba ff ff 00 00 0f


如果是在中斷上下文,則直接調用panic()【panic 輸出oops信息後將掛起系統】。如果是在進程上下文,則根據panic_on_oops的設置選擇是否panic()。panic_on_oops的缺省設置是”0″,即在Oops發生時不會進行panic()操作。可以通過sysctl進行設置:
sysctl -w kernel.panic_on_oops=1

有panic_on_oops這樣的設置,說明Oops不一定導致系統死亡,也不一定需要重新啓動系統。正如用戶程序segfault時可能還能堅持運行一樣。不過Oops一旦發生,系統已經有些不正常了,即使表面上可能還正常,不過可能有些鎖已經被佔用而無法釋放,很快會導致系統死鎖

那麼,panic()是什麼呢?panic()和用戶空間的abort()類似,簡單清理一下,就可以放心去死(reboot)了。

3. do_page_fault() -> die() -> panic()

panic會根據 kernel.panic 的設置決定 reboot 前的延時,如果 kernel.panic=0,則打開中斷,陷入死循環。反之,則在幾秒之後,reboot系統。

可以看出雖然都是死,但死因不同,死亡時的表現更是五花八門。常見的死因有:

  • 非法內存訪問 (比如訪問地址0)
  • 非法指令

有時候核心成心發出非法指令,比如BUG() (include/asm/bug.h) 中所做的,以引起Oops。類似用戶程序中調用assert()。

死亡發生的地點也很關鍵,直接導致了死亡的不同表現,比如:

  • 進程上下文
  • 中斷上下文

在中斷上下文中,由於中斷是關閉的,而且往往會佔用一些鎖,這種情況下一般除了死,沒有什麼別的辦法。

在進程上下文中要自由一些,如果運氣好的話,可以苟延殘喘一段時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章