?? (ldd) ch04-調(diào)試技術(轉(zhuǎn)載).txt
字號:
(LDD) Ch04-調(diào)試技術(轉(zhuǎn)載)
第4章 調(diào)試技術
對于任何編寫內(nèi)核代碼的人來說,最吸引他們注意的問題之一就是如何完成調(diào)試。由于
內(nèi)核是一個不與某個進程相關的功能集,其代碼不能很輕松地放在調(diào)試器中執(zhí)行,而且
也不能跟蹤。
本章介紹你可以用來監(jiān)視內(nèi)核代碼和跟蹤錯誤的技術。
用打印信息調(diào)試
最一般的調(diào)試技術就是監(jiān)視,就是在應用內(nèi)部合適的點加上printf調(diào)用。當你調(diào)試內(nèi)核
代碼的時候,你可以用printk完成這個任務。
Printk
Printk
在前些章中,我們簡單假設printk工作起來和printf很類似。現(xiàn)在是介紹一下它們之間
不同的時候了。
其中一個不同點就是,printk允許你根據(jù)它們的嚴重程度,通過附加不同的“記錄級”
來對消息分類,或賦予消息優(yōu)先級。你可以用宏來指示記錄級。例如,KERN_INFO,我們
前面已經(jīng)看到它被加在打印語句的前面,它就是一種可能的消息記錄級。記錄級宏展開
為一個字串,在編譯時和消息文本拼接在一起;這也就是為什么下面的例子中優(yōu)先級和
格式字串間沒有逗號。這有兩個printk的例子,一個是調(diào)試信息,一個是關鍵信息:
(代碼)
在<linux/kernel.h>中定義了8種記錄級別串。沒有指定優(yōu)先級的printk語句默認使用DE
FAULT_MESSAGE_LOGLEVEL優(yōu)先級,它是一個在kernel/printk.c中定義的整數(shù)。默認記錄
級的具體數(shù)值在Linux的開發(fā)期間曾變化過若干次,所以我建議你最好總是指定一個合適
的記錄級。
根據(jù)記錄級,內(nèi)核將消息打印到當前文本控制臺上:如果優(yōu)先級低于console_loglevel
這個數(shù)值的話,該消息就顯示在控制臺上。如果系統(tǒng)同時運行了klogd和syslogd,無論c
onsole_loglevel為何值,內(nèi)核都將消息追加到/var/log/messages中。
變量console_loglevel最初初始化為DEFAULT_CONSOLE_LOGLEVEL,但可以通過sys_syslo
g系統(tǒng)調(diào)用修改。如klogd的手冊所示,可以在啟動klogd時指定-c開關來修改這個變量。
g系統(tǒng)調(diào)用修改。如klogd的手冊所示,可以在啟動klogd時指定-c開關來修改這個變量。
此外,你還可以寫個程序來改變控制臺記錄級。你可以在O’Reilly站點上的源文件中找
到我寫的一個這種功能的程序,miscprogs/setlevel.c。新優(yōu)先級是通過一個1到8之間
的整數(shù)值指定的。
你也許需要在內(nèi)核失效后降低記錄級(見“調(diào)試系統(tǒng)故障”),這是因為失效處理代碼
會將console_loglevel提升到15,之后所有的消息都會出現(xiàn)在控制臺上。為看到你的調(diào)
試信息,如果你運行的是內(nèi)核2.0.x話,你需要提升記錄級。內(nèi)核2.0發(fā)行降低了MINIMUM
_CONSOLE_LOGLEVEL,而舊版本的klogd默認情況下要打印很多控制消息。如果你碰巧使
用了這個舊版本的守護進程,除非你提升記錄級,內(nèi)核2.0會比你預期的打印出更少的消
息。這就是為什么hello.c中使用了<1>標記,這樣可以保證消息顯示在控制臺上。
從1.3.43一來的內(nèi)核版本通過允許你向指定虛控制臺發(fā)送消息,藉此提供一個靈活的記
錄策略。默認情況下,“控制臺”是當前虛終端。也可以選擇不同的虛終端接收消息,
你只需向所選的虛終端調(diào)用ioctl(TIOCLINUX)。如下程序,setconsole,可以用來選擇
哪個虛終端接收內(nèi)核消息;它必須以超級用戶身份運行。如果你對ioctl還不有把握,你
可以跳過這至下一節(jié),等到讀完第5章“字符設備驅(qū)動程序的擴展操作”的“ioctl”一
節(jié)后,再回到這里讀這段代碼。
(代碼)
setconsole使用了用于Linux專用功能的特殊的ioctl命令TIOCLINUX。為了使用TIOCLINU
X,你要傳遞給它一個指向字節(jié)數(shù)組的指針。數(shù)組的第一個字節(jié)是所請求的子命令的編碼
X,你要傳遞給它一個指向字節(jié)數(shù)組的指針。數(shù)組的第一個字節(jié)是所請求的子命令的編碼
,隨后的字節(jié)依命令而不同。在setconsole中使用了子命令11,后一個字節(jié)(存放在byt
es[1]中)標別虛擬控制臺。TIOCLINUX的完成介紹可以在內(nèi)核源碼drivers/char/tty_io
..c中找到。
消息是如何記錄的
printk函數(shù)將消息寫到一個長度為LOG_BUF_LEN個字節(jié)的循環(huán)緩沖區(qū)中。然后喚醒任何等
待消息的進程,即那些在調(diào)用syslog系統(tǒng)調(diào)用或讀取/proc/kmesg過程中睡眠的進程。這
兩個訪問記錄引擎的接口是等價的。不過/proc/kmesg文件更象一個FIFO文件,從中讀取
數(shù)據(jù)更容易些。一跳簡單的cat命令就可以讀取消息。
如果循環(huán)緩沖區(qū)填滿了,printk就繞到緩沖區(qū)的開始處填寫新數(shù)據(jù),覆蓋舊數(shù)據(jù)。于是
記錄進程就丟失了最舊的數(shù)據(jù)。這個問題與利用循環(huán)緩沖區(qū)所獲得的好處相比可以忽略
不計。例如,循環(huán)緩沖區(qū)可以使系統(tǒng)在沒有記錄進程的情況下照樣運行,同時又不浪費
內(nèi)存。Linux處理消息的方法的另一個特點是,可以在任何地方調(diào)用printk,甚至在中斷
處理函數(shù)里也可以調(diào)用,而且對數(shù)據(jù)量的大小沒有限制。這個方法的唯一缺點就是可能
丟失某些數(shù)據(jù)。
如果klogd正在運行,它讀取內(nèi)核消息并將它們分派到syslogd,它隨后檢查/etc/syslog
..conf找到處理這些數(shù)據(jù)的方式。syslogd根據(jù)一個“設施”和“優(yōu)先級”切分消息;可
以使用的值定義在<sys/syslog.h>中。內(nèi)核消息根據(jù)相應printk中指定的優(yōu)先級記錄到L
OG_KERN設施中。如果klogd沒有運行,數(shù)據(jù)將保存在循環(huán)緩沖區(qū)中直到有進程來讀取數(shù)
據(jù)或數(shù)據(jù)溢出。
據(jù)或數(shù)據(jù)溢出。
如果你不希望因監(jiān)視你的驅(qū)動程序的消息而把你的系統(tǒng)記錄搞亂,你給klogd指定-f(文
件)選項或修改/etc/syslog.conf將記錄寫到另一個文件中。另一種方法是一種強硬方
法:殺掉klogd,將消息打印到不用的虛終端上*,或者在一個不用的xterm上執(zhí)行cat
/proc/kmesg顯示消息。
使用預處理方便監(jiān)視處理
在驅(qū)動程序開發(fā)早期,printk可以對調(diào)試和測試新代碼都非常有幫助。然而當你正式發(fā)
行驅(qū)動程序時,你應該去掉,或者至少關閉,這些打印語句。很不幸,你可能很快就發(fā)
現(xiàn),隨著你想不再需要那些消息并去掉它們時,你可能又要加新功能,你又需要這些消
息了。解決這些問題有幾種方法――如何從全局打開和關閉消息以及如何打開和關閉個
別消息。
下面給出了我處理消息所用的大部分代碼,它有如下一些功能:
l 可以通過在宏名字加一個字母或去掉一個字母打開或關閉每一條語句。
l 通過在編譯前修改CFLAGS變量,可以一次關閉所有消息。
l 同樣的打印語句既可以用在內(nèi)核態(tài)(驅(qū)動程序)也可以用在用戶態(tài)(演示或測
試程序)。
下面這些直接來自scull.h的代碼片斷實現(xiàn)了這些功能。
(代碼)
符合PDEBUG和PDEBUGG依賴于是否定義了SCULL_DEBUG,它們都和printf調(diào)用很類似。
為了進一步方便這個過程,在你的Makefile加上如下幾行。
(代碼)
本節(jié)所給出的代碼依賴于gcc對ANSI C預編譯器的擴展,gcc可以支持帶可變數(shù)目參數(shù)的
宏。這種對gcc的依賴并不是什么問題,因為內(nèi)核對gcc特性的依賴更強。此外,Makefil
e依賴于GNU的gmake;基于同樣的道理,這也不是什么問題。
如果你很熟悉C預編譯器,你可以將上面的定義擴展為可以支持“調(diào)試級”概念的,可以
為每級賦一個整數(shù)(或位圖),說明這一級打印多么瑣碎的消息。
但是每一個驅(qū)動程序都有它自己的功能和監(jiān)視需求。好的編程技巧會在靈活性和高效之
間找到一個權衡點,這個我就不能說哪個對你最好了。記住,預編譯器條件(還有代碼
中的常量表達式)只到編譯時運行,你必須重新編譯程序來打開或關閉消息。另一種方
法就是使用C條件語句,它在運行時運行,因此可以讓你在程序執(zhí)行期間打開或關閉消息
。這個功能很好,但每次代碼執(zhí)行系統(tǒng)都要進行額外的處理,甚至在消息關閉后仍然會
。這個功能很好,但每次代碼執(zhí)行系統(tǒng)都要進行額外的處理,甚至在消息關閉后仍然會
影響性能。有時這種性能損失是無法接受的。
個人觀點,盡管上面給出的宏迫使你每次要增加或去掉消息時都要重新編譯,重新加載
模塊,但我覺得用這些宏已經(jīng)很好了。
通過查詢調(diào)試
上一節(jié)談到了printk是如何工作的以及如何使用它。但沒有談及它的缺點。
由于syslogd會一直保持刷新它的輸出文件,每打印一行都會引起一次磁盤操作,因此過
量使用printk會嚴重降低系統(tǒng)性能。至少從syslogd的角度看是這樣的。它會將所有的數(shù)
據(jù)都一股腦地寫到磁盤上,以防在打印消息后系統(tǒng)崩潰;然而,你不想因為調(diào)試信息的
緣故而降低系統(tǒng)性能。這個問題可以通過在/etc/syslogd.conf中記錄文件的名字前加一
個波折號解決,但有時你不想修改你的配置文件。如果不這樣,你還可以運行一個非klo
gd的程序(如前面介紹的cat /proc/kmesg),但這樣并不能為正常操作提供一個合適的
環(huán)境。
與這相比,最好的方法就是在你需要信息的時候,通過查詢系統(tǒng)獲得相關信息,而不是
持續(xù)不斷地產(chǎn)生數(shù)據(jù)。事實上,每一個Unix系統(tǒng)都提供了很多工具用來獲得系統(tǒng)信息:p
s,netstat,vmstat等等。
有許多技術適合與驅(qū)動程序開發(fā)人員查詢系統(tǒng),簡而言之就是,在/proc下創(chuàng)建文件和使
用ioctl驅(qū)動程序方法。
用ioctl驅(qū)動程序方法。
使用/proc文件系統(tǒng)
Linux中的/proc文件系統(tǒng)與任何設備都沒有關系――/proc中的文件都在被讀取時有核心
創(chuàng)建的。這些文件都是普通的文本文件,它們基本上可由普通人理解,也可被工具程序
理解。例如,對于大多數(shù)Linux的ps實現(xiàn)而言,它都通過讀取/proc文件系統(tǒng)獲得進程表
信息的。/proc虛擬文件的創(chuàng)意已由若干現(xiàn)代操作系統(tǒng)使用,且非常成功。
/proc的當前實現(xiàn)可以動態(tài)創(chuàng)建i節(jié)點,允許用戶模塊為方便信息檢索創(chuàng)建如何入口點。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -