使用 printk 進行訊息日誌記錄

printk() 是 Linux 核心中最廣為人知的函式之一。它是我們用於列印訊息的標準工具,通常也是追蹤和除錯最基本的方式。如果您熟悉 printf(3),就會知道 printk() 是基於它的,儘管它有一些功能上的差異

  • printk() 訊息可以指定一個日誌級別。

  • 格式字串雖然大體上與 C99 相容,但並未遵循完全相同的規範。它有一些擴充套件和一些限制(沒有 %n 或浮點轉換說明符)。請參閱 如何正確使用 printk 格式說明符

所有 printk() 訊息都會列印到核心日誌緩衝區,這是一個透過 /dev/kmsg 匯出到使用者空間的環形緩衝區。通常使用 dmesg 來讀取它。

printk() 通常這樣使用:

printk(KERN_INFO "Message: %s\n", arg);

其中 KERN_INFO 是日誌級別(注意它與格式字串連線在一起,日誌級別不是單獨的引數)。可用的日誌級別有:

名稱

字串

別名函式

KERN_EMERG

“0”

pr_emerg()

KERN_ALERT

“1”

pr_alert()

KERN_CRIT

“2”

pr_crit()

KERN_ERR

“3”

pr_err()

KERN_WARNING

“4”

pr_warn()

KERN_NOTICE

“5”

pr_notice()

KERN_INFO

“6”

pr_info()

KERN_DEBUG

“7”

pr_debug()pr_devel() (如果定義了 DEBUG)

KERN_DEFAULT

“”

KERN_CONT

“c”

pr_cont()

日誌級別指定了訊息的重要性。核心根據訊息的日誌級別和當前的 console_loglevel(一個核心變數)來決定是否立即顯示該訊息(將其列印到當前控制檯)。如果訊息優先順序高於(日誌級別值較低)console_loglevel,則訊息將被列印到控制檯。

如果省略日誌級別,訊息將以 KERN_DEFAULT 級別列印。

您可以使用以下命令檢查當前的 console_loglevel

$ cat /proc/sys/kernel/printk
4        4        1        7

結果顯示了 current(當前)、default(預設)、minimum(最小)和 boot-time-default(啟動時預設)日誌級別。

要更改當前的 console_loglevel,只需將所需的級別寫入 /proc/sys/kernel/printk。例如,要將所有訊息列印到控制檯:

# echo 8 > /proc/sys/kernel/printk

另一種方法,使用 dmesg

# dmesg -n 5

將 console_loglevel 設定為列印 KERN_WARNING (4) 或更嚴重的訊息到控制檯。更多資訊請參閱 dmesg(1)

作為 printk() 的替代,您可以使用 pr_*() 別名進行日誌記錄。這組宏將日誌級別嵌入到宏名稱中。例如:

pr_info("Info message no. %d\n", msg_num);

列印一條 KERN_INFO 訊息。

除了比等效的 printk() 呼叫更簡潔之外,它們還可以透過 pr_fmt() 宏為格式字串使用一個通用定義。例如,在原始檔頂部(在任何 #include 指令之前)定義此內容:

#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__

將會在該檔案中的每個 pr_*() 訊息前加上產生訊息的模組和函式名。

出於除錯目的,還有兩個條件編譯宏:pr_debug()pr_devel(),除非定義了 DEBUG(或者在 pr_debug() 的情況下還定義了 CONFIG_DYNAMIC_DEBUG),否則它們不會被編譯。

函式參考

pr_fmt

pr_fmt (fmt)

由 pr_*() 宏用於生成 printk 格式字串

引數

fmt

從 pr_*() 宏傳遞的格式字串

描述

此宏可用於為 pr_*() 宏生成統一的格式字串。常見的用途是在檔案中所有 pr_*() 訊息前加上一個通用字串。例如,在原始檔頂部定義此內容:

#define pr_fmt(fmt) KBUILD_MODNAME “: “ fmt

將會在檔案中所有 pr_info、pr_emerg 等訊息前加上模組名稱。

printk

printk (fmt, ...)

列印核心訊息

引數

fmt

格式字串

...

可變引數

描述

這是 printk()。它可以在任何上下文中呼叫。我們希望它能正常工作。

如果啟用了 printk 索引,則從 printk_index_wrap 呼叫 _printk()。否則,printk 只是 #定義為 _printk。

我們嘗試獲取 console_lock。如果成功,就很容易——我們記錄輸出並呼叫控制檯驅動程式。如果未能獲取訊號量,我們將輸出放入日誌緩衝區並返回。當前持有 console_sem 的程序將在 console_unlock() 中注意到新的輸出;並在釋放鎖之前將其傳送到控制檯。

這種延遲列印的一個影響是,呼叫 printk() 然後更改 console_loglevel 的程式碼可能會出現問題。這是因為 console_loglevel 是在實際列印時檢查的。

另請參閱:printf(3)

有關 C99 格式字串擴充套件的更多資訊,請參閱 vsnprintf() 文件。

pr_emerg

pr_emerg (fmt, ...)

列印緊急級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_EMERG 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_alert

pr_alert (fmt, ...)

列印警報級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_ALERT 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_crit

pr_crit (fmt, ...)

列印關鍵級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_CRIT 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_err

pr_err (fmt, ...)

列印錯誤級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_ERR 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_warn

pr_warn (fmt, ...)

列印警告級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_WARNING 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_notice

pr_notice (fmt, ...)

列印通知級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_NOTICE 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_info

pr_info (fmt, ...)

列印資訊級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_INFO 日誌級別的 printk。它使用 pr_fmt() 來生成格式字串。

pr_cont

pr_cont (fmt, ...)

在同一行繼續上一條日誌訊息。

引數

fmt

格式字串

...

格式字串的引數

描述

此宏擴充套件為具有 KERN_CONT 日誌級別的 printk。它只應用於在沒有換行符('n')的情況下繼續日誌訊息。否則,它將預設回退到 KERN_DEFAULT 日誌級別。

pr_devel

pr_devel (fmt, ...)

有條件地列印除錯級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

如果定義了 DEBUG,此宏將擴充套件為具有 KERN_DEBUG 日誌級別的 printk。否則,它不執行任何操作。

它使用 pr_fmt() 來生成格式字串。

pr_debug

pr_debug (fmt, ...)

有條件地列印除錯級別訊息

引數

fmt

格式字串

...

格式字串的引數

描述

如果設定了 CONFIG_DYNAMIC_DEBUG,此宏將擴充套件為 dynamic_pr_debug()。否則,如果定義了 DEBUG,它等同於具有 KERN_DEBUG 日誌級別的 printk。如果未定義 DEBUG,它不執行任何操作。

它使用 pr_fmt() 來生成格式字串(dynamic_pr_debug() 內部使用 pr_fmt())。