久综合色-久综合网-玖草影视-玖草资源在线-亚洲黄色片子-亚洲黄色片在线观看

Hello! 歡迎來到小浪云!


Linux內核調試方法


avatar
小浪云 2025-01-03 120

在開始解決一個bug之前,我們需要進行一些必要的準備工作:

首先,確認存在一個bug,并記錄下出現該bug的內核版本信息。分析確定這個bug首次出現在哪個版本很關鍵,因為這將大大促進問題的解決。通過執行二分法查找,可以有效地縮小引入bug的版本范圍。

Linux內核調試方法

深入理解內核代碼會對調試過程有極大的幫助,有時候還需要一些好運,以便bug能被成功復現。如果能夠發現復現bug的規律,那么離揭示問題的根本原因就更近一步了;簡化系統配置。通過逐步排除可能導致bug的因素,來縮小問題范圍。

內核中的bug

內核中的bug也是多種多樣的。它們的產生有無數的原因,同時表象也變化多端。從隱藏在源代碼中的錯誤到展現在目擊者面前的bug,其發作往往是一系列連鎖反應的事件才可能觸發的。雖然內核調試有一定的困難,但是通過你的努力和理解,說不定你會喜歡上這樣的挑戰。

內核調試配置選項

學習編寫驅動程序要構建安裝自己的內核(標準主線內核)。最重要的原因之一是:內核開發者已經建立了多項用于調試的功能。但是由于這些功能會造成額外的輸出,并導致能量下降,因此發行版廠商通常會禁止發行版內核中的調試功能。

內核配置

為了實現內核調試,在內核配置上增加了幾項:

Kernel?hacking?--->?[*]?Magic?SysRq?key?[*]?Kernel?debugging?[*]?  ?Debug?slab?memory?allocaTIons?[*]?Spinlock?and?rw-lock?debugging:?basic?checks?[*]?Spinlock?debugging:?sleep-inside-spinlock ? ?checking?[*]?Compile?the?kernel?with?debug?info?Device?Drivers?--->?Generic?Driver?Options?--->?[*]??Driver?Core?verbose?debug ? ?messages?General?setup?--->?[*]?Configure?standard?kernel?features?(for?small?systems)?--->?[*]????Load?all?symbols?for? ? ?debugging/ksymoops 

調試原子操作

從內核2.5開發,為了檢查各類由原子操作引發的問題,內核提供了極佳的工具。
內核提供了一個原子操作計數器,它可以配置成,一旦在原子操作過程中,經常進入睡眠或者做了一些可能引起睡眠的操作,就打印警告信息并提供追蹤線索。
所以,包括在使用鎖的時候調用schedule(),正使用鎖的時候以阻塞方式請求分配內存等,各種潛在的bug都能夠被探測到。

下面這些選項可以最大限度地利用該特性:

CONFIG_PREEMPT?=?y? CONFIG_DEBUG_KERNEL?=?y? CONFIG_KLLSYMS?=?y?CONFIG_SPINLOCK_SLEEP?=?y 

引發bug并打印信息

BUG()和BUG_ON()

一些內核調用可以用來方便標記bug,提供斷言并輸出信息。最常用的兩個是BUG()和BUG_ON()。定義在中:

#ifndef?HAVE_ARCH_BUG?#define?BUG()?do?{? ??printk("BUG:?failure?at?%s:%d/%s()!?",?__FILE__,?__LINE__,?__FUNCTION__);??panic("BUG!");?/*?引發更嚴重的錯誤,不但打印錯誤消息,而且 ?? ??整個系統業會掛起?*/?}?while?(0)?#endif?#ifndef?HAVE_ARCH_BUG_ON?#define?BUG_ON(condiTIon)? ?? ?? do?{?  ??if?(unlikely(condiTIon))?BUG();? ?? }while(0)?  ??#endif 

當調用這兩個宏的時候,它們會引發OOPS,導致的回溯和錯誤消息的打印。
※ 可以把這兩個調用當作斷言使用,如:BUG_ON(bad_thing);

WARN(x) 和 WARN_ON(x)

而WARN_ON則是調用dump_stack,打印信息,不會OOPS。定義在中:

#ifndef?__WARN_TAINT#ifndef?__ASSEMBLY__extern?void?warn_slowpath_fmt(  ??const?char?*file,?const?int?line,?const?char?*fmt,?...)?__attribute__((format(printf,?3,?4)));extern?void? ?? ??warn_slowpath_fmt_taint(const?char?*file,?const?int?line,?unsigned?taint,?const?char?*fmt,?...)?__attribute__((format(printf, ?? ??4,?5)));extern?void?warn_slowpath_null(const?char?*file,?const?int?line);#define?WANT_WARN_ON_SLOWPATH#endif#define?__WARN()? ?? ??warn_slowpath_null(__FILE__,?__LINE__)#define?__WARN_printf(arg...)?warn_slowpath_fmt(__FILE__,?__LINE__,?arg)#define? ?? ??__WARN_printf_taint(taint,?arg...)?warn_slowpath_fmt_taint(__FILE__,?__LINE__,?taint,?arg)#else#define?__WARN()? ?? ??__WARN_TAINT(TAINT_WARN)#define?__WARN_printf(arg...)?do?{?printk(arg);?__WARN();??????}?while?(0)#define? ?? ??__WARN_printf_taint(taint,?arg...)?do?{?printk(arg);?__WARN_TAINT(taint);???}? ?? ?? while?(0)??#endif#ifndef?WARN_ON#define?WARN_ON(condition)?({????int?__ret_warn_on?=?!!(condition);?if?(unlikely(__ret_warn_on))?  __WARN();?  unlikely(__ret_warn_on);?})#endif#ifndef?WARN#define?WARN(condition,?format...)?({???int?__ret_warn_on?=?!!(condition);?if?  (unlikely(__ret_warn_on))?__WARN_printf(format);?   unlikely(__ret_warn_on);?})#endif 

dump_stack()

有些時候,只需要在終端上打印一下棧的回溯信息來幫助你調試。這時可以使用dump_stack()。這個函數只是在終端上打印寄存器上下文和函數的跟蹤線索。

if?(!debug_check)?{??printk(KERN_DEBUG?“provide?some?information…/n”);??dump_stack();??} 

printk()

內核提供的格式化打印函數。

printk函數的健壯性

健壯性是printk最容易被接受的一個特質,幾乎在任何地方,任何時候內核都可以調用它(中斷上下文、進程上下文、持有鎖時、多處理器處理時等)。

printk函數脆弱之處

在系統啟動過程中,終端初始化之前,在某些地方是不能調用的。如果真的需要調試系統啟動過程最開始的地方,有以下方法可以使用:

使用串口調試,將調試信息輸出到其他終端設備。

使用early_printk(),該函數在系統啟動初期就有打印能力。但它只支持部分硬件體系。

LOG等級

printk和printf一個主要的區別就是前者可以指定一個LOG等級。內核根據這個等級來判斷是否在終端上打印消息。內核把比指定等級高的所有消息顯示在終端。
可以使用下面的方式指定一個LOG級別:

printk(KERN_CRIT?“Hello,?world!?”); 

注意,第一個參數并不一個真正的參數,因為其中沒有用于分隔級別(KERN_CRIT)和格式字符的逗號(,)。KERN_CRIT本身只是一個普通的字符串(事實上,它表示的是字符串 “”;表 1 列出了完整的日志級別清單)。

作為預處理程序的一部分,C 會自動地使用一個名為 字符串串聯 的功能將這兩個字符串組合在一起。組合的結果是將日志級別和用戶指定的格式字符串包含在一個字符串中。

內核使用這個指定LOG級別與當前終端LOG等級console_loglevel來決定是不是向終端打印。下面是可使用的LOG等級:

#define?KERN_EMERG?""?/*?system?is?unusable?*/#define?KERN_ALERT?""?/*?action?must?be?taken?immediately?*/?#define?  KERN_CRIT?""?/*?critical?conditions?*/#define?KERN_ERR?""?/*?error?conditions?*/#define?KERN_WARNING?""?/*?warning?  conditions?*/#define?KERN_NOTICE?""?/*?normal?but?significant?condition?*/#define?KERN_INFO?""?/*?informational?*/#define?  KERN_DEBUG?""?/*?debug-level?messages?*/#define?KERN_DEFAULT?""?/*?Use?the?default?kernel?loglevel?*/ 

注意,如果調用者未將日志級別提供給 printk,那么系統就會使用默認值 KERN_WARNING “”(表示只有KERN_WARNING 級別以上的日志消息會被記錄)。由于默認值存在變化,所以在使用時最好指定LOG級別。有LOG級別的一個好處就是我們可以選擇性的輸出LOG。

比如平時我們只需要打印KERN_WARNING級別以上的關鍵性LOG,但是調試的時候,我們可以選擇打印KERN_DEBUG等以上的詳細LOG。而這些都不需要我們修改代碼,只需要通過命令修改默認日志輸出級別:

mtj@ubuntu?:~$?cat?/proc/sys/kernel/printk4?4?1?7mtj@ubuntu?:~$?cat?/proc/sys/kernel/printk_delay0?mtj@ubuntu?:~$?cat?  /proc/sys/kernel/printk_ratelimit5?mtj@ubuntu?:~$?cat?/proc/sys/kernel/printk_ratelimit_burst10 

第一項定義了 printk API 當前使用的日志級別。這些日志級別表示了控制臺的日志級別、默認消息日志級別、最小控制臺日志級別和默認控制臺日志級別。printk_delay 值表示的是 printk 消息之間的延遲毫秒數(用于提高某些場景的可讀性)。

注意,這里它的值為 0,而它是不可以通過的 /proc 設置的。

printk_ratelimit 定義了消息之間允許的最小時間間隔(當前定義為每 5 秒內的某個內核消息數)。消息數量是由 printk_ratelimit_burst 定義的(當前定義為 10)。

如果您擁有一個非正式內核而又使用有帶寬限制的控制臺設備(如通過串口), 那么這非常有用。注意,在內核中,速度限制是由調用者控制的,而不是在printk 中實現的。如果一個 printk 如果用戶要求進行速度限制,那么該用戶就需要調用printk_ratelimit 函數。

記錄緩沖區

內核消息都被保存在一個LOG_BUF_LEN大小的環形隊列中。
關于LOG_BUF_LEN定義:

#define __LOG_BUF_LEN (1

※ 變量CONFIG_LOG_BUF_SHIFT在內核編譯時由配置文件定義,對于i386平臺,其值定義如下(在
linux26/arch/i386/defconfig中):

CONFIG_LOG_BUF_SHIFT=18

記錄緩沖區操作:
① 消息被讀出到用戶空間時,此消息就會從環形隊列中刪除。
② 當消息緩沖區滿時,如果再有printk()調用時,新消息將覆蓋隊列中的老消息。
③ 在讀寫環形隊列時,同步問題很容易得到解決。

※ 這個紀錄緩沖區之所以稱為環形,是因為它的讀寫都是按照環形隊列的方式進行操作的。

syslogd/klogd

在標準的Linux系統上,用戶空間的守護進程klogd從紀錄緩沖區中獲取內核消息,再通過syslogd守護進程把這些消息保存在系統日志文件中。klogd進程既可以從/proc/kmsg文件中,也可以通過syslog()系統調用讀取這些消息。默認情況下,它選擇讀取/proc方式實現。klogd守護進程在消息緩沖區有新的消息之前,一直處于阻塞狀態。一旦有新的內核消息,klogd被喚醒,讀出內核消息并進行處理。默認情況下,處理例程就是把內核消息傳給syslogd守護進程。syslogd守護進程一般把接收到的消息寫入/var/log/messages文件中。不過,還是可以通過/etc/syslog.conf文件來進行配置,可以選擇其他的輸出文件。

dmesg

dmesg 命令也可用于打印和控制內核緩沖區。這個命令使用 klogctl 系統調用來讀取內核環緩沖區,并將它轉發到標準輸出(stdout)。這個命令也可以用來清除內核環緩沖區(使用 -c 選項),設置控制臺日志級別(-n 選項),以及定義用于讀取內核日志消息的緩沖區大小(-s 選項)。注意,如果沒有指定緩沖區大小,那么 dmesg 會使用 klogctl 的SYSLOG_ACTION_SIZE_BUFFER 操作確定緩沖區大小。

注意

a) 雖然printk很健壯,但是看了源碼你就知道,這個函數的效率很低:做字符拷貝時一次只拷貝一個字節,且去調用console輸出可能還產生中斷。所以如果你的驅動在功能調試完成以后做性能測試或者發布的時候千萬記得盡量減少printk輸出,做到僅在出錯時輸出少量信息。否則往console輸出無用信息影響性能。
b) printk的臨時緩存printk_buf只有1K,所有一次printk函數只能記錄

內核printk和日志系統的總體結構

動態調試

動態調試是通過動態的開啟和禁止某些內核代碼來獲取額外的內核信息。
首先內核選項CONFIG_DYNAMIC_DEBUG應該被設置。所有通過pr_debug()/dev_debug()打印的信息都可以動態地顯示或不顯示。
可以通過簡單的查詢語句來篩選需要顯示的信息。

-源文件名

-函數名

-行號(包括指定范圍的行號)

-模塊名

-格式化字符串

將要打印信息的格式寫入/dynamic_debug/control中。

nullarbor:~ # echo ‘file svcsock.c line 1603 +p’ > /dynamic_debug/control

參考:
1 內核日志及printk結構淺析 — Tekkaman Ninja
2 內核日志:API 及實現
3 printk實現分析
4 dynamic-debug-howto.txt

內存調試工具

MEMWATCH

MEMWATCH 由 Johan Lindh 編寫,是一個開放源代碼 C 語言內存錯誤檢測工具,您可以自己下載它。只要在代碼中添加一個頭文件并在 gcc 語句中定義了 MEMWATCH 之后,您就可以跟蹤程序中的內存泄漏和錯誤了。MEMWATCH 支持ANSIC,它提供結果日志紀錄,能檢測雙重釋放(double-free)、錯誤釋放(erroneous free)、沒有釋放的內存(unfreedmemory)、溢出和下溢等等。

清單 1. 內存樣本(test1.c)

#include??#include??#include?"memwatch.h"?int?main(void){?char?*ptr1;?char?*ptr2;?ptr1?=?malloc(512);?ptr2?=?malloc(512);?ptr2?=?  ptr1;?free(ptr2);?free(ptr1);?} 

清單 1 中的代碼將分配兩個 512 字節的內存塊,然后指向第一個內存塊的指針被設定為指向第二個內存塊。結果,第二個內存塊的地址丟失,從而產生了內存泄漏。
現在我們編譯清單 1 的 memwatch.c。

下面是一個?makefile?示例:   test1gcc?-DMEMWATCH?-DMW_STDIO?test1.c?memwatchc?-o?test1 

當您運行 test1 程序后,它會生成一個關于泄漏的內存的報告。清單 2 展示了示例 memwatch.log 輸出文件。

清單 2. test1 memwatch.log 文件

MEMWATCH?2.67?Copyright?(C)?1992-1999?Johan?Lindh...double-free:??test1.c(15),?0x80517b4?was?freed?from?  test1.c(14)...unfreed:??test1.c(11),?512?bytes?at?0x80519e4{FE?FE?FE?FE?FE?FE?FE?FE?FE?FE?FE?FE?..............}Memory?usage?  statistics?(global):?N)umber?of?allocations?made:?2?L)argest?memory?usage?:?1024?T)otal?of?all?alloc()?calls:?1024?U)nfreed?  bytes?totals?:?512 

MEMWATCH 為您顯示真正導致問題出現的信息。如果您釋放一個已經釋放過的指針,它會告訴您。對于沒有釋放的內存也一樣。日志結尾部分顯示統計信息,包括泄露了多少內存,使用了多少內存,以及總共分配了多少內存。

YAMD

YAMD 軟件包由 Nate Eldredge 編寫,可以查找 C++ 和 C++ 動態的、與內存分配有關的問題。在撰寫本文時,YAMD 的最新版本為 0.32。請下載 yamd-0.32.tar.gz。執行 make 命令來構建程序;然后執行 make install 命令安裝程序并設置工具。

一旦您下載了 YAMD 之后,請在 test1.c 上使用它。請刪除 #include memwatch.h 并對 makefile 進行如下小小的修改:

?

使用 YAMD 的 test1

gcc -g test1.c -o test1

清單 3 展示了來自 test1 上的 YAMD 的輸出。

清單 3. 使用 YAMD 的 test1 輸出

YAMD?version?0.32?Executable:?/usr/src/test/yamd-0.32/test1...?INFO:?Normal?allocation?of?this?blockAddress?0x40025e00,?size?  512...?INFO:?Normal?allocation?of?this?blockAddress?0x40028e00,?size?512...?INFO:?Normal?deallocation?of?this?blockAddress?  0x40025e00,?size?512...?ERROR:?Multiple?freeing?Atfree?of?pointer?already?freedAddress?0x40025e00,?size?512...?WARNING:?Memory?  leakAddress?0x40028e00,?size?512?WARNING:?Total?memory?leaks:1?unfreed?allocations?totaling?512?bytes***?Finished?at?Tue?...?  10:07:15?2002?Allocated?a?grand?total?of?1024?bytes?2?allocationsAverage?of?512?bytes?per?allocation?Max?bytes?allocated?at?one?  time:?102424?K?alloced?internally?/?12?K?mapped?now?/?8?K?maxVirtual?program?size?is?1416?KEnd. 

YAMD 顯示我們已經釋放了內存,而且存在內存泄漏。讓我們在清單 4 中另一個樣本程序上試試 YAMD。

清單 4. 內存代碼(test2.c)

#include??#include??int?main(void){?char?*ptr1;?char?*ptr2;?char?*chptr;?int?i?=?1;?ptr1?=?malloc(512);?ptr2?=?malloc(512);?  chptr?=?(char?*)malloc(512);?for?(i;?i?'S';?}?ptr2?=?ptr1;?free(ptr2);?free(ptr1);?free(chptr);?} 

您可以使用下面的命令來啟動 YAMD:

./run-yamd?/usr/src/test/test2/test2 

清單 5 顯示了該樣本程序 test2 上使用 YAMD 得到的輸出。YAMD 告訴我們在 for 循環中有“越界(out-of-bounds)”的情況。

清單 5. 使用 YAMD 的 test2 輸出

Running?/usr/src/test/test2/test2Temp?output?to?/tmp/yamd-out.1243*********?./run-yamd:?line?101:?1248?Segmentation?fault?(core?  dumped)?YAMD?version?0.32Starting?run:?/usr/src/test/test2/test2?Executable:?/usr/src/test/test2/test2Virtual?program?size?is?  1380?K...?INFO:?Normal?allocation?of?this?blockAddress?0x40025e00,?size?512...?INFO:?Normal?allocation?of?this?blockAddress?  0x40028e00,?size?512...?INFO:?Normal?allocation?of?this?blockAddress?0x4002be00,?size?512ERROR:?Crash...Tried?to?write?address?  0x4002c000Seems?to?be?part?of?this?block:Address?0x4002be00,?size?512...?Address?in?question?is?at?offset?512?(out?of?bounds)  Will?dump?core?after?checking?heap.Done. 

MEMWATCH 和 YAMD 都是很有用的調試工具,不過它們的使用方法有所不同。對于 MEMWATCH,您需要添加包含文件memwatch.h 并打開兩個編譯時間標記。對于鏈接(link)語句,YAMD 只需要 -g 選項。

Electric Fence

多數 Linux 分發版包含一個 Electric Fence 包,不過您也可以選擇下載它。Electric Fence 是一個由 Bruce Perens 編寫的malloc()調試庫。它就在您分配內存后分配受保護的內存。如果存在 fencepost 錯誤(超過數組末尾運行),程序就會產生保護錯誤,并立即結束。通過結合 Electric Fence 和 gdb,您可以精確地跟蹤到哪一行試圖訪問受保護內存。ElectricFence 的另一個功能就是能夠檢測內存泄漏。

strace

strace 命令是一種強大的工具,它能夠顯示所有由用戶空間程序發出的系統調用。strace 顯示這些調用的參數并返回符號形式的值。strace 從內核接收信息,而且不需要以任何特殊的方式來構建內核。將跟蹤信息發送到應用程序及內核開發者都很有用。在清單 6 中,分區的一種格式有錯誤,清單顯示了 strace 的開頭部分,內容是關于調出創建文件系統操作(mkfs )的。strace 確定哪個調用導致問題出現。

清單 6. mkfs 上 strace 的開頭部分

execve("/sbin/mkfs.jfs",?["mkfs.jfs",?"-f",?"/dev/test1"],?&...open("/dev/test1",?O_RDWR|O_LARGEFILE)?=?4stat64("/dev/test1",?  {st_mode=&,?st_rdev=makedev(63,?255),?...})?=?0ioctl(4,?0x40041271,?0xbfffe128)?=?-1?EINVAL?(Invalid?argument)write(2,?  "mkfs.jfs:?warning?-?cannot?setb"?...,?98mkfs.jfs:??warning?-cannot?set?blocksize?on?block?device?/dev/test1:?Invalid?argument?)?  =?98stat64("/dev/test1",?{st_mode=&,?st_rdev=makedev(63,?255),?...})?=?0open("/dev/test1",?O_RDONLY|O_LARGEFILE)?=?5ioctl(5,?  0x80041272,?0xbfffe124)?=?-1?EINVAL?(Invalid?argument)write(2,?"mkfs.jfs:?can't?determine?device"...,??..._exit(1)?=?? 

清單 6 顯示 ioctl 調用導致用來格式化分區的 mkfs 程序失敗。ioctl BLKGETSIZE64 失敗。( BLKGET-SIZE64 在調用 ioctl的源代碼中定義。) BLKGETSIZE64 ioctl 將被添加到 Linux 中所有的設備,而在這里,邏輯卷管理器還不支持它。因此,如果BLKGETSIZE64 ioctl 調用失敗,mkfs 代碼將改為調用較早的 ioctl 調用;這使得 mkfs 適用于邏輯卷管理器。

OOPS

OOPS(也稱 Panic)消息包含系統錯誤的細節,如 CPU 寄存器的內容等。是內核告知用戶有不幸發生的最常用的方式。

內核只能發布OOPS,這個過程包括向終端上輸出錯誤消息,輸出寄存器保存的信息,并輸出可供跟蹤的回溯線索。通常,發送完OOPS之后,內核會處于一種不穩定的狀態。
OOPS的產生有很多可能原因,其中包括內存訪問越界或非法的指令等。

※ 作為內核的開發者,必定將會經常處理OOPS。

※ OOPS中包含的重要信息,對所有體系結構的機器都是完全相同的:寄存器上下文和回溯線索(回溯線索顯示了導致錯誤發生的函數調用鏈)。

ksymoops

在 Linux 中,調試系統崩潰的傳統方法是分析在發生崩潰時發送到系統控制臺的 Oops 消息。一旦您掌握了細節,就可以將消息發送到 ksymoops 使用程序,它將試圖將代碼轉換為指令并將棧值映射到內核符號。

※ 如:回溯線索中的地址,會通過ksymoops轉化成名稱可見的函數名。

ksymoops需要幾項內容:Oops 消息輸出、來自正在運行的內核的 System.map 文件,還有 /proc/ksyms、vmlinux和/proc/modules。

關于如何使用 ksymoops,內核源代碼
/usr/src/linux/Documentation/oops-tracing.txt 中或 ksymoops 手冊頁上有完整的說明可以參考。Ksymoops 返回編代碼部分,指出發生錯誤的指令,并顯示一個跟蹤部分表明代碼如何被調用。
首先,將 Oops 消息保存在一個文件中以便通過 ksymoops 使用程序運行它。清單 7 顯示了由安裝 JFS 文件系統的 mount命令創建的 Oops 消息。

清單 7. ksymoops 處理后的 Oops 消息

ksymoops?2.4.0?on?i686?2.4.17.?Options?used...?15:59:37?sfb1??kernel:?Unable?to?handle?kernel?NULL?pointer?dereference?atvirtual?  address?0000000...?15:59:37?sfb1?kernel:?c01588fc...?15:59:37?sfb1?kernel:?*pde?=?0000000...?15:59:37?sfb1?kernel:?Oops:?0000...?   15:59:37?sfb1?kernel:?CPU:?0...?15:59:37?sfb1?kernel:?EIP:?0010:[jfs_mount+60/704]...?15:59:37?sfb1?kernel:?Call?Trace:?  [jfs_read_super+287/688]?[get_sb_bdev+563/736]?[do_kern_mount+189/336]?[do_add_mount+35/208][do_page_fault+0/1264]...?15:59:37?  sfb1?kernel:?Call?Trace:?[]......?15:59:37?sfb1?kernel:?[>EIP;?c01588fc?Code;?c01588fc?00000000?  :Code;?c01588fc??6:?55?push?%ebp 

接下來,您要確定 jfs_mount 中的哪一行代碼引起了這個問題。Oops 消息告訴我們問題是由位于偏移地址 3c 的指令引起的。做這件事的辦法之一就是對 jfs_mount.o 文件使用 objdump 使用程序,然后查看偏移地址 3c。Objdump 用來反匯編模塊函數,看看您的 C 源代碼會產生什么匯編指令。清單 8 顯示了使用 objdump 后您將看到的內容,接著,我們查看jfs_mount 的 C 代碼,可以看到空值是第 109 行引起的。偏移地址 3c 之所以這么重要,是因為 Oops 消息將該處標識為引起問題的位置。

清單 8. jfs_mount 匯編程序清單

109?printk("%d?",*ptr);objdump?jfs_mount.ojfs_mount.o:?file?format?elf32-i386Disassembly?of?section?.text:00000000?:?0:55?push?  %ebp?...?2c:?e8?cf?03?00?00?call?400?31:?89?c3?mov?%eax,%ebx?33:?58?pop?%eax?34:?85?db?test?%ebx,%ebx?36:?0f?85?55?02?00?00?jne?  291?0x291>?3c:?8b?2d?00?00?00?00?mov?0x0,%ebp?

kallsyms

開發版2.5內核引入了kallsyms特性,它可以通過定義CONFIG_KALLSYMS編譯選項啟用。該選項可以載入內核鏡像所對應的內存地址的符號名稱(即函數名),所以內核可以打印解碼之后的跟蹤線索。相應,解碼OOPS也不再需要System.map和ksymoops工具了。另外, 這樣做,會使內核變大些,因為地址對應符號名稱必須始終駐留在內核所在內存上。

#cat?/proc/kallsyms?c0100240?T?_stext?c0100240?t?run_init_process?c0100240?T?stext?c0100269?t?init?… 

Kdump

什么是 kexec ?

Kexec 是實現 kdump 機制的關鍵,它包括 2 一是組成部分:一是內核空間的系統調用 kexec_load,負責在生產內核(production kernel 或 first kernel)啟動時將捕獲內核(capture kernel 或 sencond kernel)加載到指定地址。二是用戶空間的工具 kexec-tools,他將捕獲內核的地址傳遞給生產內核,從而在系統崩潰的時候能夠找到捕獲內核的地址并運行。沒有 kexec 就沒有 kdump。先有 kexec 實現了在一個內核中可以啟動另一個內核,才讓 kdump 有了用武之地。kexec 原來的目的是為了節省時間 kernel 開發人員重啟系統的時間,誰能想到這個“偷懶”的技術卻孕育了最成功的內存轉存機制呢?

什么是 kdump ?

Kdump 的概念出現在 2005 左右,是迄今為止最可靠的內核轉存機制,已經被主要的 linux? 廠商選用。kdump是一種先進的基于 kexec 的內核崩潰轉儲機制。當系統崩潰時,kdump 使用 kexec 啟動到第二個內核。第二個內核通常叫做捕獲內核,以很小的內存啟動以捕獲轉儲鏡像。第一個內核保留了內存的一部分給第二個內核啟動用。由于 kdump 利用 kexec 啟動捕獲內核,繞過了 BIOS,所以第一個內核的內存得以保留。這是內核崩潰轉儲的本質。

kdump 需要兩個不同目的的內核,生產內核和捕獲內核。生產內核是捕獲內核服務的對象。捕獲內核會在生產內核崩潰時啟動起來,與相應的 ramdisk 一起組建一個微環境,用以對生產內核下的內存進行收集和轉存。

如何使用 kdump

構建系統和 dump-capture 內核,此操作有 2 種方式可選:

1)構建一個單獨的自定義轉儲捕獲內核以捕獲內核轉儲;

2) 或者將系統內核本身作為轉儲捕獲內核,這就不需要構建一個單獨的轉儲捕獲內核。

方法(2)只能用于可支持可重定位內核的體系結構上;目前 i386,x86_64,ppc64 和 ia64 體系結構支持可重定位內核。構建一個可重定位內核使得不需要構建第二個內核就可以捕獲轉儲。但是可能有時想構建一個自定義轉儲捕獲內核以滿足特定要求。

如何訪問捕獲內存

在內核崩潰之前所有關于核心映像的必要信息都用 ELF 格式編碼并存儲在保留的內存區域中。ELF 頭所在的物理地址被作為命令行參數(fcorehdr=)傳遞給新啟動的轉儲內核。

在 i386 體系結構上,啟動的時候需要使用物理內存開始的 640K,而不管操作系統內核轉載在何處。因此,這個640K 的區域在重新啟動第二個內核的時候由 kexec 備份。

在第二個內核中,“前一個系統的內存”可以通過兩種方式訪問:

1) 通過 /dev/oldmem 這個設備接口

一個“捕捉”設備可以使用“raw”(裸的)方式 “讀”這個設備文件并寫出到文件。這是關于內存的 “裸”的數據轉儲,同時這些分析 / 捕捉工具應該足夠“智能”從而可以知道從哪里可以得到正確的信息。ELF 文件頭(通過命令行參數傳遞過來的 elfcorehdr)可能會有幫助。

2) 通過 /proc/vmcore。

這個方式是將轉儲輸出為一個 ELF 格式的文件,并且可以使用一些文件拷貝命令(比如 cp,scp 等)將信息讀出來。同時,gdb 可以在得到的轉儲文件上做一些調試(有限的)。這種方式保證了內存中的頁面都以正確的途徑被保存 ( 注意內存開始的 640K 被重新映射了 )。

kdump 的優勢

1) 高可靠性

崩潰轉儲數據可從一個新啟動內核的上下文中獲取,而不是從已經崩潰內核的上下文。

2) 多版本支持

LKCD(Linux Kernel Crash Dump),netdump,diskdump 已被納入 LDPs(Linux Documen-tation Project) 內核。SUSE 和 RedHat 都對 kdump 有技術支持。

配置 kdump

安裝軟件包和實用程序

Kdump 用到的各種工具都在 kexec-tools 中。kernel-debuginfo 則是用來分析 vmcore 文件。從 rhel5 開始,kexec-tools 已被默認安裝在發行版。而 novell 也在 sles10 發行版中把 kdump 集成進來。所以如果使用的是rhel5 和 sles10 之后的發行版,那就省去了安裝 kexec-tools 的步驟。而如果需要調試 kdump 生成的 vmcore文件,則需要手動安裝 kernel-debuginfo 包。檢查安裝包操作:

3.3.2?參數相關設置?uli13lp1:/??#?rpm?-qa|grep?kexec?kexec-tools-2.0.0-53.43.10?uli13lp1:/?#?rpm?-qa?'kernel*debuginfo*'?kernel-  default-debuginfo-3.0.13-0.27.1?kernel-ppc64-debuginfo-3.0.13-0.27.1 

系統內核設置選項和轉儲捕獲內核配置選擇在《使用 Crash 工具分析 Linux dump 文件》一文中已有說明,在此不再贅述。僅列出內核引導參數設置以及配置文件設置。

1) 修改內核引導參數,為啟動捕獲內核預留內存

通過下面的方法來配置 kdump 使用的內存大小。添加啟動參數”crashkernel=Y@X”,這里,Y 是為 kdump 捕捉內核保留的內存,X 是保留部分內存的開始位置。

對于 i386 和 x86_64, 編輯 /etc/grub.conf, 在內核行的最后添加”crashkernel=128M” 。

對于 ppc64,在 /etc/yaboot.conf 最后添加”crashkernel=128M”。

在 ia64, 編輯 /etc/elilo.conf,添加”crashkernel=256M”到內核行。

2) kdump 配置文件

kdump 的配置文件是 /etc/kdump.conf(RHEL6.2);/etc/sysconfig/kdump(SLES11 sp2)。每個文件頭部都有選項說明,可以根據使用需求設置相應的選項。

啟動 kdump 服務

在設置了預留內存后,需要重啟機器,否則 kdump 是不可使用的。啟動 kdump 服務:

Rhel6.2:

#?chkconfig?kdump?on?#?service?kdump?status?Kdump?is?operational?#?service?kdump?start 

SLES11SP2:

#?chkconfig?boot.kdump?on?#?service?boot.kdump?start 

測試配置是否有效

可以通過 kexec 加載內核鏡像,讓系統準備好去捕獲一個崩潰時產生的 vmcore??梢酝ㄟ^ sysrq 強制系統崩潰。

#?echo?c?>?/proc/sysrq-trigger 

這造成內核崩潰,如配置有效,系統將重啟進入 kdump 內核,當系統進程進入到啟動 kdump 服務的點時,vmcore 將會拷貝到你在 kdump 配置文件中設置的位置。RHEL 的缺省目錄是 : /var/crash;SLES 的缺省目錄是 : /var/log/dump。然后系統重啟進入到正常的內核。一旦回復到正常的內核,就可以在上述的目錄下發現 vmcore 文件,即內存轉儲文件??梢允褂弥鞍惭b的 kernel-debuginfo 中的 crash 工具來進行分析(crash 的更多詳細用法將在本系列后面的文章中有介紹)。

#?crash?/usr/lib/debug/lib/modules/2.6.17-1.2621.el5/vmlinux?/var/crash/2006-08-23-15:34/vmcore?crash>?bt 

載入“轉儲捕獲”內核

需要引導系統內核時,可使用如下步驟和命令載入“轉儲捕獲”內核:

kexec?-p?--initrd=for-dump-capture-kernel>?--args-linux?--append="root=?init?1?irqpoll" 

裝載轉儲捕捉內核的注意事項:

轉儲捕捉內核應當是一個 vmlinux 格式的映像(即是一個未壓縮的 ELF 映像文件),而不能是 bzImage 格式;

默認情況下,ELF 文件頭采用 ELF64 格式存儲以支持那些擁有超過 4GB 內存的系統。但是可以指定“–elf32-core-headers”標志以強制使用 ELF32 格式的 ELF 文件頭。這個標志是有必要注意的,一個重要的原因就是:當前版本的 GDB 不能在一個 32 位系統上打開一個使用 ELF64 格式的 vmcore 文件。ELF32 格式的文件頭不能使用在一個“沒有物理地址擴展”(non-PAE)的系統上(即:少于 4GB 內存的系統);

一個“irqpoll”的啟動參數可以減低由于在“轉儲捕獲內核”中使用了“共享中斷”技術而導致出現驅動初始化失敗這種情況發生的概率 ;

必須指定 ,指定的格式是和要使用根設備的名字。具體可以查看 mount 命令的輸出;“init 1”這個命令將啟動“轉儲捕捉內核”到一個沒有網絡支持的單用戶模式。如果你希望有網絡支持,那么使用“init 3”。

后記

Kdump 是一個強大的、靈活的內核轉儲機制,能夠在生產內核上下文中執行捕獲內核是非常有價值的。本文僅介紹在 RHEL6.2 和 SLES11 中如何配置 kdump。

相關閱讀

主站蜘蛛池模板: 日韩经典欧美精品一区 | a级做爰视频免费观看 | 狠狠色狠狠色综合久久第一次 | 亚洲欧洲精品国产二码 | 久色免费视频 | 91影视永久福利免费观看 | 久草热在线观看 | 欧美在线视频一区 | 免费特级毛片 | 一区二区三区高清在线 | 中国黄色一级大片 | 国产精品久久免费 | 正在播真实出轨炮对白 | 精品国产v无码大片在线观看 | 手机看片欧美 | 在线中文字幕日韩 | avtt加勒比手机版天堂网 | 亚欧美视频| 欧美在线成人免费国产 | www.自拍| 欧美一级毛片片aa视频 | 成人在线免费看 | 欧美激情第一欧美在线 | 97视频在线观看免费播放 | 国产三级精品久久三级国专区 | 成人软件18免费 | 午夜亚洲国产成人不卡在线 | 久久一日本道色综合久 | 最爽的乱淫片免费 | 久久久久久久91精品免费观看 | 国产高颜值露脸在线观看 | 亚洲国产一区二区a毛片 | 亚洲日韩中文字幕天堂不卡 | 日本国产最新一区二区三区 | 久草在线免费看 | 男女视频免费在线观看 | 一级国产视频 | 一区二区日韩欧美 | 日本亚洲成高清一区二区三区 | www.亚洲精品| 成人国产网站 |