?? (ldd) ch04-調試技術(轉載).txt
字號:
Control-PrScr(Show_State)
針對系統里的每一個處理器打印一行信息,同時還打印內部進程樹。對當前進程進行標
記。
RightAlt-PrScr(Show_Registers)
由于它可以打印按鍵時的處理器寄存器內容,它是系統掛起時最重要的一個鍵了。如果
有當前內核的系統表的話,查看指令計數器以及它如何隨時間變化,對了解代碼在何處
有當前內核的系統表的話,查看指令計數器以及它如何隨時間變化,對了解代碼在何處
循環非常有幫助。
如果想將這些函數映射到不同的鍵上,每一個函數名都可以做為參數傳遞給loadkeys。
鍵盤映射表可以任意修改(這是“策略無關的”)。
如果console_loglevel足夠到的話,這些函數打印的消息會出現在控制臺上。如果不是
你運行了一個舊klogd和一個新內核的話,默認記錄級應該足夠了。如果沒有出現消息,
你可以象以前說的那樣提升記錄級。“足夠高”的具體值與你使用的內核版本有關。對
于Linux 2.0或更新的版本來說是5。
即便當系統掛起時,消息也會打印到控制臺上,確認記錄級足夠高是非常重要的。消息
是在產生中斷時生成的,因此即便有錯的進程不釋放CPU也可以運行――當然,除非中斷
被屏蔽了,不過如果發生這種情況既不太可能也非常不幸。
有時系統看起來象是掛起了,但其實不是。例如,如果鍵盤因某種奇怪的原因被鎖住了
就會發生這種情況。這種假掛起可以通過查看你為探明此種情況而運行的程序輸出來判
斷。我有一個程序會不斷地更新LED顯示器上的時鐘,我發現這個對于驗證調度器尚在運
行非常有用。你可以不必依賴外部設備就可以檢查調度器,你可以實現一個程序讓鍵盤L
ED閃爍,或是不斷地打開關閉軟盤馬達,或是不斷觸動揚聲器――不過我個人認為,通
常的蜂鳴聲很煩人,應該盡量避免。看看ioctl命令KDMKTONE。O’Reilly FTP站點上的
例子程序(misc-progs/heartbeat.c)中有一個是讓鍵盤LED不斷閃爍的。
如果鍵盤不接收輸入了,最佳的處理手段是從網絡登錄在系統中,殺掉任何違例的進程
,或是重新設置鍵盤(用kdb_mode -a)。然而,如果你沒有網絡可用來恢復的話,發現
系統掛起是由鍵盤鎖死造成的一點兒用也沒有。如果情況確實是這樣,你應該配置一種
替代輸入設備,至少可以保證正常地重啟系統。對于你的計算機來說,關閉系統或重啟
比起所謂的按“大紅鈕”要更方便一些,至少它可以免去長時間地fsck掃描磁盤。
這種替代輸入設備可以是游戲桿或是鼠標。在sunsite.edu.cn上有一個游戲桿重啟守護
進程,gpm-1.10或更新的鼠標服務器可以通過命令行選項支持類似的功能。如果鍵盤沒
有鎖死,但是卻誤入“原始”模式,你可以看看kdb包中文檔介紹的一些小技巧。我建議
最好在問題出現以前就看看這些文檔,否則就太晚了。另一種可能是配置gpm-root菜單
,增添一個“reboot”或“reset keyboard”菜單項;gpm-root一個響應控制鼠標事件
的守護進程,它用來在屏幕上顯示菜單和執行所配置的動作。
最好,你會可以按“留意安全鍵”(SAK),一個用于將系統恢復為可用狀態的特殊鍵。
由于不是所有的實現都能用,當前Linux版本的默認鍵盤表中沒有為此鍵特設一項。不過
你還是可以用loadkeys將你的鍵盤上的一個鍵映射為SAK。你應該看看drivers/char目錄
中的SAK實現。代碼中的注釋解釋了為什么這個鍵在Linux 2.0中不是總能工作,這里我
就不多說了。
不過,如果你運行版本2.1.9或是更新的版本,你就可以使用非常可靠地留意安全鍵了。
此外,2.1.43及后續版本內核還有一個編譯選項選擇是否打開“SysRq魔法鍵”;我建議
你看一看drivers/char/sysrq.c中的代碼并使用這項新技術。
你看一看drivers/char/sysrq.c中的代碼并使用這項新技術。
如果你的驅動程序真的將系統掛起了,而且你有不知道在哪插入schedule調用,最佳的
處理方法就是加一些打印消息,并將它們打印到控制臺上(通過修改console_loglevel
變量值)。在重演掛起過程時,最好將所有的磁盤都以只讀方式安裝在系統上。如果磁
盤是只讀的或沒有安裝,就不會存在破壞文件系統或使其進入不一致狀態的危險。至少
你可以避免在復位系統后運行fsck。另一中方法就是使用NFS根計算機來測試模塊。在這
種情況下,由于NFS服務器管理文件系統的一致性,而它又不會受你的驅動程序的影響,
你可以避免任何的文件系統崩潰。
使用調試器
最后一種調試模塊的方法就是使用調試器來一步步地跟蹤代碼,查看變量和機器寄存器
的值。這種方法非常耗時,應該盡可能地避免。不過,某些情況下通過調試器對代碼進
行細粒度的分析是非常有益的。在這里,我們所說的被調試的代碼運行在內核空間――
除非你遠程控制內核,否則不可能一步步跟蹤內核,這會使很多事情變得更加困難。由
于遠程控制很少用到,我們最后介紹這項技術。所幸的是,在當前版本的內核中可以查
看和修改變量。
在這一級上熟練地使用調試器需要精通gdb命令,對匯編碼有一定了解,并且有能夠將源
碼與優化后的匯編碼對應起來的能力。
不幸的是,gdb更適合與調試核心而不是模塊,調試模塊化的代碼需要更多的技術。這更
多的技術就是kdebug包,它利用gdb的“遠程調試”接口控制本地內核。我將在介紹普通
多的技術就是kdebug包,它利用gdb的“遠程調試”接口控制本地內核。我將在介紹普通
調試器后介紹kdebug。
使用gdb
gdb在探究系統內部行為時非常有用。啟動調試器時必須假想內核就是一個應用程序。除
了指定內核文件名外,你還應該在命令行中提供內存鏡象文件的名字。典型的gdb調用如
下所示:
(代碼)
第一個參數是未經壓縮的內核可執行文件(在你編譯完內核后,這個文件在/usr/src/li
nux目錄中)的名字。只有x86體系結構有zImage文件(有時稱為vmlinuz),它是一種解
決Intel處理器實模式下只有640KB限制的一種技巧;而無論在哪個平臺上,vmlinux都是
你所編譯的未經壓縮的內核。
gdb命令行的第二個參數是是內存鏡象文件的名字。與其他在/proc下的文件類似,/proc
/kcore也是在被讀取時產生的。當read系統調用在/proc文件系統執行時,它映射到一個
用于數據生成而不是數據讀取的函數上;我們已在“使用/proc文件系統”一節中介紹了
這個功能。系統用kcore來表示按內存鏡象文件格式存儲的內核“可執行文件”;由于它
要表示整個內核地址空間,它是一個非常巨大的文件,對應所有的物理內存。利用gdb,
你可以通過標準gdb命令查看內核標量。例如,p jiffies可以打印從系統啟動到當前時
刻的時鐘滴答數。
當你從gdb打印數據時,內核還在運行,不同數據項會在不同時刻有不同的數值;然而,
gdb為了優化對內存鏡象文件的訪問會將已經讀到的數據緩存起來。如果你再次查看jiff
ies變量,你會得到和以前相同的值。緩存變量值防止額外的磁盤操作對普通內存鏡象文
件來說是對的,但對“動態”內存鏡象文件來說就不是很方便了。解決方法是在你想刷
新gdb緩存的時候執行core-file /proc/kcore命令;調試器將使用新的內存鏡象文件并
廢棄舊信息。但是,讀新數據時你并不總是需要執行core-file命令;gdb以1KB的尺度讀
取內存鏡象文件,僅僅緩存它所引用的若干塊。
你不能用普通gdb做的是修改內核數據;由于調試器需要在訪問內存鏡象前運行被調試程
序,它是不會去修改內存鏡象文件的。當調試內核鏡象時,執行run命令會導致在執行若
干指令后導致段違例。出于這個原因,/proc/kcore都沒有實現write方法。
如果你用調試選項(-g)編譯了內核,結果產生的vmlinux比沒有用-g選項的更適合于gd
b。不過要注意,用-g選項編譯內核需要大量的磁盤空間――支持網絡和很少幾個設備和
文件系統的2.0內核在PC上需要11KB。不過不管怎樣,你都可以生成zImage文件并用它來
其他系統:在生成可啟動鏡象時由于選項-g而加入的調試信息最終都被去掉了。如果我
有足夠的磁盤空間,我會一致打開-g選項的。
在非PC計算機上則有不同的方法。在Alpha上,make boot會在生成可啟動鏡象前將調試
信息去掉,所以你最終會獲得vmlinux和vmlinux.gz兩個文件。gdb可以使用前者,但你
只能用后者啟動。在Sparc上,默認情況下內核(至少是2.0內核)不會被去掉調試信息
,所以你需要在將其傳遞給silo(Sparc的內核加載器)前將調試信息去掉,這樣才能啟
,所以你需要在將其傳遞給silo(Sparc的內核加載器)前將調試信息去掉,這樣才能啟
動。由于尺寸的問題,無論milo(Alpha的內核加載器)還是silo都不能啟動未去掉調試
信息的內核。
當你用-g選項編譯內核并且用vmlinux和/proc/kcore一起使用調試器,gdb可以返回很多
有關內核內部結構的信息。例如,你可以使用類似于這樣的命令,p *module_list,p
*module_list->next和p *chrdevs[4]->fops等顯示這些結構的內容。如果你手頭有內核
映射表和源碼的話,這些探測命令是非常有用的。
另一個gdb可以在當前內核上執行的有用任務是,通過disassemble命令(它可以縮寫)
或是“檢查指令”(x/i)命令反匯編函數。disassemble命令的參數可以是函數名或是
內存區范圍,而x/i則使用一個內存地址做為參數,也可以用符號名。例如,你可以用x/
20i反匯編20條指令。注意,你不能反匯編一個模塊的函數,這是因為調試器處理vmlinu
x,它并不知道你的模塊的信息。如果你試圖用模塊的地址反匯編代碼,gdb很有可能會
報告“不能訪問xxxx處的內存(Cannot access memory at xxxx)”。基于同樣的原因
,你不查看屬于模塊的數據項。如果你知道你的變量的地址,你可以從/dev/mem中讀出
它的值,但很難弄明白從系統內存中分解出的數據是什么含義。
如果你需要反匯編模塊函數,你最好對用objdump工具處理你的模塊文件。很不幸,該工
具只能對磁盤上的文件進行處理,而不能對運行中的模塊進行處理;因此,objdump中給
出的地址都是未經重定位的地
--
或是“檢查指令”(x/i)命令反匯編函數。disassemble命令的參數可以是函數名或是
內存區范圍,而x/i則使用一個內存地址做為參數,也可以用符號名。例如,你可以用x/
20i反匯編20條指令。注意,你不能反匯編一個模塊的函數,這是因為調試器處理vmlinu
x,它并不知道你的模塊的信息。如果你試圖用模塊的地址反匯編代碼,gdb很有可能會
報告“不能訪問xxxx處的內存(Cannot access memory at xxxx)”。基于同樣的原因
,你不查看屬于模塊的數據項。如果你知道你的變量的地址,你可以從/dev/mem中讀出
它的值,但很難弄明白從系統內存中分解出的數據是什么含義。
如果你需要反匯編模塊函數,你最好對用objdump工具處理你的模塊文件。很不幸,該工
具只能對磁盤上的文件進行處理,而不能對運行中的模塊進行處理;因此,objdump中給
出的地址都是未經重定位的地
--
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -