![](http://img.inpai.com.cn/2023/0410/20230410091623448.jpg)
很多內核開發(fā)者喜歡的調試工具是printk,在Linux內核中,使用printk()函數(shù)來打印信息,它與C庫的printf()函數(shù)類似。
printk()與printf()的一個重要區(qū)別是: printk()提供輸出等級。內核會根據(jù)這個等級來判斷是否在終端或者串口中輸出。
【資料圖】
路徑:include/linux/kern_levels.h
#define KERN_EMERG KERN_SOH "0" /* 最高輸出等級,系統(tǒng)可能處于不可用的狀態(tài) */#define KERN_ALERT KERN_SOH "1" /* 緊急和理科需要處理的輸出 */#define KERN_CRIT KERN_SOH "2" /* 緊急情況 */#define KERN_ERR KERN_SOH "3" /* 發(fā)生錯誤的情況 */#define KERN_WARNING KERN_SOH "4" /* 警告 */#define KERN_NOTICE KERN_SOH "5" /* 重要的提示 */#define KERN_INFO KERN_SOH "6" /* 提示信息 */#define KERN_DEBUG KERN_SOH "7" /* 調試輸出 */
Linux內核為printk定義了8個輸出等級,KERN_EMERG
等級最高,KERN_DEBUG
等級最低。在配置內核時,由一個宏來設置系統(tǒng)默認的輸出等級CONFIG_MESSAGE_LOGLEVEL_DEFAULT
,通常這個默認輸出等級為4
,因此只有輸出等級高于4時才會輸出到終端或者串口,即只有KERN_EMERG~KERN_ERR滿足這個條件。
通常在產(chǎn)品開發(fā)階段,會把系統(tǒng)默認等級設置為最低,以便在開發(fā)測試階段可以暴露更多的問題和調試信息,在發(fā)布產(chǎn)品時再把輸出等級設置為0或者4。
# cat /proc/sys/kernel/printk //printk默認有4個等級7 4 1 7
四個數(shù)字分表代表:
控制臺輸出等級
默認消息等級
最低輸出等級
默認控制臺輸出等級
在系統(tǒng)運行時,我們也可以修改系統(tǒng)的輸出等級。打開所有的內核輸出:
echo 8 > /proc/sys/kernel/printk //打開所有的內核輸出
另外,還可以通過在啟動內核時傳遞commandline
給內核的方法來修改系統(tǒng)默認的輸出等級。例如,使用uboot引導內核時,可以在uboot傳參的bootargs
參數(shù)上,加上“loglevel=8
”,這樣在系統(tǒng)啟動時,就打開了所有內核輸出。
在實際調試中,printk()
可以和printf()
一樣,直接輸出一條字符串。
不過為了更好的顯示一些調試信息,可以加上函數(shù)名字(__func__)
和代碼行號(__LINE__)
,例如:
printk(KERN_EMERG"figo:%s, %d", __func__, __LINE__);
在雙引號""前加上輸出等級KERN_EMERG
,代表輸出等級為0
。
另外,在使用printk()的時候需要注意輸出格式,否則在編譯時會出現(xiàn)很多的警告。printk的輸出格式:
數(shù)據(jù)類型 | printk格式符 |
---|---|
int | %d或%x |
unsigned int | %u或%x |
long | %ld或%lx |
long long | %lld或%llx |
unsigned long long | %llu或%llx |
size_t | %zu或%zx |
size_t | %zd或%zx |
函數(shù)指針 | %pf |
在使用printk的時候需要手動添加輸出等級KERN_INFO、KERN_WARNING等,這樣還是有些麻煩。因此,Linux內核也對printk進行了進一步的封裝。
Linux內核將每一個輸出等級封裝為pr_xx()函數(shù),例如,輸出等級KERN_INFO
封裝為pr_info(),輸出等級KERN_WARNING
封裝為pr_warn()。具體如下:
#define pr_emerg(fmt, ...) \\ printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)#define pr_alert(fmt, ...) \\ printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)#define pr_crit(fmt, ...) \\ printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)#define pr_err(fmt, ...) \\ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)#define pr_warn(fmt, ...) \\ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)#define pr_notice(fmt, ...) \\ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)#define pr_info(fmt, ...) \\ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)#define pr_err(fmt, ...) \\ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
這里對輸出等級為KERN_DEBUG
的封裝是比較特殊的,因為debug等級比較常用,內核對pr_debug()分為了三種情況:
如果設置了 CONFIG_DYNAMIC_DEBUG
,則此pr_debug()擴展為 dynamic_pr_debug(),主要用于 動態(tài)輸出。否則,如果定義了 DEBUG
宏,則它等同于具有 KERN_DEBUG
日志級別的 printk。 如果未定義 DEBUG,則它什么都不做。
pr_debug()的定義如下:
/* If you are writing a driver, please use dev_dbg instead*/#if defined(CONFIG_DYNAMIC_DEBUG) || \\ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))#include < linux/dynamic_debug.h >/** * pr_debug - Print a debug-level message conditionally * @fmt: format string * @...: arguments for the format string * * This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is * set. Otherwise, if DEBUG is defined, it"s equivalent to a printk with * KERN_DEBUG loglevel. If DEBUG is not defined it does nothing. * * It uses pr_fmt() to generatethe format string (dynamic_pr_debug() uses * pr_fmt() internally). */#define pr_debug(fmt, ...) \\ dynamic_pr_debug(fmt, ##__VA_ARGS__)#elif defined(DEBUG)#define pr_debug(fmt, ...) \\ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)#else#define pr_debug(fmt, ...) \\ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)#endif
標簽: