使用 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())。