英语

使用 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 只是 #defined 到 _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())。