Linux內核棧信息獲取與理解
1 內核棧獲取
C語言的函數調用,是通過棧來實現的。如下圖所示:
內核異常或死機時,經常在內核日誌中看到打印的棧信息和寄存器值。從函數棧信息,我們可以知道函數間的調用關係,從棧和寄存器數據,還可以得到各個變量、參數的值。對Linux內核理解和故障定位非常有幫助。
若希望打印出棧信息的函數中加上dump_stack()即可,dump_stack()已導出到內核符號表中,可直接調用。
00276: / *
00277: * The architecture- independent dump_stack generator
00278: */
00279: void dump_stack(void)
00280: {
00281: unsigned long stack;
00282:
00283: show_trace(current, NULL, &stack);
00284: }
00285:
00286: EXPORT_SYMBOL(dump_stack);
00287:
若是內核代碼,則需要重新編譯內核。若是內核模塊,編譯後,重新加載該模塊。注意要控制好打印棧信息的次數和頻度。內核中有些代碼是大量被執行的,這時要控制打印次數,否則系統長時間無法響應。
下面棧信息是在文件fs/jbd/transaction.c,函數journal_dirty_data()中加入dump_stack()函數後的獲取到的棧信息。
2 內核棧信息的理解
對於Linux內核棧信息來說,也主要是函數的調用關係。
如下面棧信息:
對於語句:
[<c03743c8>] smp_scan_config+0xa9/0xcb
因爲是最後一個函數,c03743c8是dump_stack()函數的地址。
linux:~ # cat /proc/kallsyms |grep smp_scan_config
c037431f t smp_scan_config
通過查找內核符號表,我們看到smp_scan_config()函數在內核中的地址是c037431f。
“+0xa9/0xcb”中0xa9含義是當前函數在上一層函數內的偏移量。對於本句來說,就是dump_stack()函數在smp_scan_config()內核的偏移量爲0xa9,行首打印的地址是c037431f + a9 = c03743c8 。“0xcb”表示smp_scan_config()函數代碼大小爲0xcb。
同理,我們知道在find_smp_config()函數中調用了smp_scan_config()函數,調用該函數的位置是代碼c037441c處,偏移量爲0x32。find_smp_config()函數大小爲0x5c。
打印內核棧信息的原理是:將棧中的值都作地址對待,然後根據地址,首先查看該地址是否屬於內核代碼(text)段或初始代碼(inittext)段中的函數。若是,則打印函數信息。
if (is_kernel_text(addr) || is_kernel_inittext(addr))
00165: const char *kallsyms_lookup(unsigned long addr,
00166: unsigned long *symbolsize,
00167: unsigned long *offset,
00168: char **modname, char *namebuf)
… …
00179: if ((all_var && is_kernel(addr)) ||
00180: (! all_var && (is_kernel_text(addr) ||
00181: is_kernel_inittext(addr)|| is_kernel_extratext(addr)))) {
… …
若不是內核代碼(text)段或初始代碼(inittext)段中的函數,那麼進而查找是爲模塊(驅動)中的函數。若是,則打印函數信息。
00224: / * see if it's in a module */
00225: msym = module_address_lookup(addr, symbolsize, offset,
modname);
00226: if (msym)
00227: return strncpy(namebuf, msym, KSYM_NAME_LEN);
若既不是內核代碼(text)段或初始代碼(inittext)段中的函數,也不是模塊(驅動)中的函數;那麼什麼信息都不打印,繼續取棧中的值,直到棧底。