內(nèi)核是Linux系統(tǒng)的“心臟”——一旦它出bug,小則功能異常,大則系統(tǒng)崩潰、死機。但內(nèi)核bug往往藏在百萬行代碼中,想快速定位、修復(fù)絕非易事。

好在Linux內(nèi)核官方提供了一份詳細的《bug狩獵手冊》(kernel.org/doc/html/latest/admin-guide/bug-hunting.html),從識別日志信號到提交修復(fù)補丁,全程拆解實用方法。今天我們就基于這份權(quán)威文檔,梳理一套“內(nèi)核bug排查實操指南”,幫你高效解決內(nèi)核故障。
一、先看懂內(nèi)核的“求救信號”:棧跟蹤與Oops信息
當(dāng)內(nèi)核遇到bug時,不會“沉默”——它會輸出棧跟蹤(stack dump)日志,告訴你“哪里出了問題”。這類日志通常分兩種場景:
1.常見的“警告型”日志(WARNING)
比如文檔中給出的例子,內(nèi)核會明確標(biāo)出出錯的CPU、進程、代碼位置:
------------[cuthere ]------------WARNING: CPU: 1 PID: 28102 at kernel/module.c:1108 module_put+0x57/0x70Modules linkedin: dvb_usb_gp8psk(-) dvb_usb dvb_core nvidia_drm(PO) ...CPU: 1 PID: 28102 Comm: rmmod Tainted: P WC O 4.8.4-build.1#1Hardware name: MSI MS-7309/MS-7309, BIOS V1.12 02/23/2009...Call Trace: # 函數(shù)調(diào)用棧,記錄bug觸發(fā)時的代碼執(zhí)行路徑[] ? dump_stack+0x44/0x64 [] ? __warn+0xfa/0x120 [] ? module_put+0x57/0x70 # 關(guān)鍵:出錯函數(shù)及偏移 ...
2.嚴(yán)重的“崩潰型”日志(Oops/BUG)
如果bug導(dǎo)致內(nèi)核無法繼續(xù)運行,會輸出帶“BUG”或“Oops”的日志,比如空指針引用:
BUG: unable to handle kernelNULLpointer dereference at (null)IP: [] iret_exc+0x7d0/0xa59# IP=指令指針,指向出錯代碼地址 Oops:0002[#1] PREEMPT SMP...
3.日志中的“模塊標(biāo)記”要注意
日志里“Modules linked in”后的模塊名,會帶特殊標(biāo)記,暗示模塊狀態(tài):
?(PO):模塊處于“待處理”狀態(tài)(Pending);
?(-):模塊正在卸載;
?(+):模塊正在加載;
?Tainted: P WC O:內(nèi)核被“污染”(比如加載了非開源模塊,影響調(diào)試)。
二、別讓關(guān)鍵日志溜走:找到Oops信息的3種場景
想定位bug,首先得拿到完整的Oops日志——內(nèi)核會把日志存在不同地方,分場景獲?。?/span>
1.系統(tǒng)還能操作:從常規(guī)日志文件拿
內(nèi)核默認通過klogd把日志傳給syslogd,存到這些位置:
?傳統(tǒng)系統(tǒng):/var/log/messages(路徑由/etc/syslog.conf配置);
?systemd系統(tǒng):用journalctl命令查看(比如journalctl -k只看內(nèi)核日志)。
如果klogd進程意外退出,還能直接讀內(nèi)核緩沖區(qū):
# 把緩沖區(qū)日志存到文件dmesg > kernel_bug.log# 或?qū)崟r讀?。ò碈trl+C停止)cat/proc/kmsg > kernel_bug.log
2.系統(tǒng)崩潰卡死:3種“救命”方法
如果機器完全凍住,無法輸入命令,試試這3招:
?應(yīng)急方案:手抄屏幕日志(或拍照),重啟后整理。若日志滾屏太快,可重啟時加vga=791(高分辨率模式)顯示更多內(nèi)容(需開啟vesafb驅(qū)動,早期啟動階段的bug無效);
?提前準(zhǔn)備:用串口控制臺(參考文檔Documentation/admin-guide/serial-console.rst)——把兩臺機器用串口線連接,另一臺用Minicom等工具捕獲日志,適合長期調(diào)試;
?專業(yè)方案:開啟Kdump(內(nèi)核崩潰轉(zhuǎn)儲)——提前配置后,崩潰時會通過kexec啟動備用內(nèi)核,從內(nèi)存中提取日志(具體看Documentation/admin-guide/kdump/gdbmacros.txt)。
三、核心步驟:精準(zhǔn)定位bug的代碼行
拿到Oops日志后,下一步是找到“具體哪行代碼出了問題”。文檔推薦兩種工具,gdb最常用,objdump可備用。
1.首選工具:gdb(需開啟調(diào)試信息)
gdb能直接把Oops中的內(nèi)存地址,翻譯成“文件名+行號”——但前提是內(nèi)核編譯時開啟了**CONFIG_DEBUG_INFO**(調(diào)試信息)。
步驟1:開啟內(nèi)核調(diào)試信息
在kernel源碼目錄下,執(zhí)行命令開啟配置:
# 關(guān)閉COMPILE_TEST,開啟DEBUG_KERNEL和DEBUG_INFO./scripts/config -d COMPILE_TEST -e DEBUG_KERNEL -e DEBUG_INFO# 重新編譯內(nèi)核(生成帶調(diào)試信息的vmlinux文件)make vmlinux
步驟2:用gdb定位代碼
根據(jù)Oops日志中的關(guān)鍵信息(EIP地址或函數(shù)偏移),用gdb解析:
?情況1:有EIP地址(比如日志中EIP: 0060:[
gdbvmlinux # 加載帶調(diào)試信息的內(nèi)核文件(gdb) l *0xc021e50e # 查看該地址對應(yīng)的代碼
?情況2:有函數(shù)偏移(比如日志中EIP is at vt_ioctl+0xda8/0x1482):
gdbvmlinux(gdb) l *vt_ioctl+0xda8 # 查看vt_ioctl函數(shù)偏移0xda8的代碼# 輸出會直接指向文件和行號,比如:# 0x1888 is in vt_ioctl (drivers/tty/vt/vt_ioctl.c:293)
步驟3:模塊級定位
如果bug在加載的模塊中(比如日志中dvb_usb_adapter_frontend_exit+0x3a/0x70 [dvb_usb]),直接加載模塊文件解析:
# 加載dvb-usb模塊的.o文件gdb drivers/media/usb/dvb-usb/dvb-usb.o(gdb) l *dvb_usb_adapter_frontend_exit+0x3a # 定位模塊內(nèi)代碼
2.備用工具:objdump(無調(diào)試信息也能用)
如果沒開啟CONFIG_DEBUG_INFO,或只有模塊文件,可用objdump反匯編代碼,間接定位問題。
基本用法(查看帶源碼的反匯編)
# -r:顯示重定位信息;-S:混合顯示源碼和匯編;-l:顯示行號objdump -r -S -l net/dccp/ipv4.o
極端情況:無源碼時
如果連源碼都沒有,可提取Oops日志中“Code:”后的字節(jié)碼,手動反匯編:
1.把Code:后的字節(jié)(比如44 24 04 e8 6f ...)存到foo.s文件:
.text.globl foofoo:.byte0x44,0x24,0x04,0xe8,0x6f, ...
1.編譯并反匯編:
gcc-c -o foo.o foo.sobjdump --disassemble foo.o # 查看匯編代碼,推斷邏輯
1.簡化操作:用內(nèi)核自帶腳本scripts/decodecode自動處理(支持多架構(gòu))。
四、報告bug:讓維護者快速接手
定位到bug后,若自己無法修復(fù),需向上游報告——關(guān)鍵是“找對人”,讓負責(zé)該模塊的維護者看到。
1.用腳本找維護者:get_maintainer.pl
內(nèi)核源碼中的scripts/get_maintainer.pl,能直接輸出文件的維護者、郵件列表:
# 查看drivers/media/usb/gspca/sonixj.c的維護信息./scripts/get_maintainer.pl --bug -f drivers/media/usb/gspca/sonixj.c
輸出結(jié)果會包含:
?模塊維護者(比如Hans Verkuil
?子系統(tǒng)維護者(比如Mauro Carvalho Chehab
?相關(guān)郵件列表(比如linux-media@vger.kernel.org,模塊專屬列表);
?內(nèi)核通用列表(linux-kernel@vger.kernel.org)。
2.報告優(yōu)先級:先bug tracker,再郵件
?若輸出中有“bug reporting URIs”(bug跟蹤鏈接),優(yōu)先在跟蹤系統(tǒng)提交;
?若無,發(fā)送郵件到“模塊專屬郵件列表”,并抄送維護者;
?完全沒頭緒時,直接發(fā)linux-kernel@vger.kernel.org(通用列表,覆蓋所有維護者)。
五、修復(fù)bug:從定位到提交的最后一步
若你有編程能力,可嘗試修復(fù)bug——提交補丁時,務(wù)必先讀Documentation/process/submitting-patches.rst,遵守內(nèi)核代碼規(guī)范(比如補丁格式、commit信息寫法),這能大幅提高補丁被接受的概率。
畢竟開源的核心是協(xié)作,你的一個小補丁,可能讓成千上萬臺Linux機器更穩(wěn)定~
最后:klogd的小技巧
klogd(內(nèi)核日志守護進程)是調(diào)試的“隱形助手”,注意兩點:
1.用1.3-pl3以上版本的sysklogd包,支持地址自動解析;
2.它會通過兩種方式解析地址:
?靜態(tài)解析:用System.map文件(內(nèi)核符號表);
?動態(tài)解析:自動獲取加載模塊的符號表(支持動態(tài)調(diào)試模塊bug);
1.模塊加載/卸載后,可重啟klogd刷新符號表(具體看klogd手冊)。
總結(jié)
內(nèi)核bug調(diào)試看似復(fù)雜,但只要跟著“識別日志→獲取日志→定位代碼→報告/修復(fù)”的流程走,再借助gdb、get_maintainer.pl等工具,就能從“無從下手”變成“有條理排查”。
這份指南的所有方法都來自內(nèi)核官方文檔,權(quán)威且實用——下次遇到內(nèi)核bug時,不妨按這個流程試試,說不定你就是解決問題的關(guān)鍵人物~
如果有內(nèi)核調(diào)試的經(jīng)驗,歡迎在評論區(qū)分享你的小技巧~
-
嵌入式
+關(guān)注
關(guān)注
5208文章
20620瀏覽量
336695 -
內(nèi)核
+關(guān)注
關(guān)注
4文章
1474瀏覽量
43088 -
Linux
+關(guān)注
關(guān)注
88文章
11806瀏覽量
219493
發(fā)布評論請先 登錄
從 Linux 內(nèi)核的角度談線程棧和進程棧
移植NXP官方linux 5.4內(nèi)核到i.MX6ULL開發(fā)板
Linux內(nèi)核的作用
從小白到大牛:Linux嵌入式系統(tǒng)開發(fā)的完整指南
關(guān)于Linux 從應(yīng)用程序開發(fā)到內(nèi)核開發(fā)的指南!
從0到1,教你徹底學(xué)透RT-Thread
對Linux的進程內(nèi)核棧的認識
如何才能編譯Linux的內(nèi)核
教你們?nèi)绾问褂胑BPF追蹤LINUX內(nèi)核
如何使用BPF對Linux內(nèi)核進行實時跟蹤
系統(tǒng)調(diào)用:用戶棧與內(nèi)核棧的切換(上)
Linux內(nèi)核bug狩獵指南:從棧跟蹤到修復(fù),官方文檔教你搞定系統(tǒng)核心故障
評論