Linux kernel printk 以及 pr_err, dev_err 等包装函数

pr_err() 等包装函数

在 Linux 内核里打印信息,除了使用 printk,还可以用 pr_err() 等包装函数,比如:

pr_err("page allocation failed\n");

这个调用相当于 printk(KERN_ERR "page allocation failed\n"); ,当然写起来更简洁。

从内核头文件 include/linux/printk.h 里看到,对于每一个 log level,都有包装函数对应:

  • KERN_EMERG: pr_emerg()
  • KERN_ALERT: pr_alert()
  • KERN_CRIT: pr_crit()
  • KERN_ERR: pr_err()
  • KERN_WARNING: pr_warn()
  • KERN_NOTICE: pr_notice()
  • KERN_INFO: pr_info()
  • KERN_DEBUG: pr_debug()

pr_fmt 宏

观察这些包装函数的定义,比如 pr_err:

#define pr_err(fmt, ...) \
    printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)

除了包装 printk + KERN_ERR 以外,还用到了 pr_fmt 这个宏,而在 pr_fmt 的注释里,是这样建议的:

...
 * This macro can be used to generate a unified format string for pr_*()
 * macros. A common use is to prefix all pr_*() messages in a file with a common
 * string. For example, defining this at the top of a source file:
 *
 *     #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 *
 * would prefix all pr_info, pr_emerg... messages in the file with the module
 * name.
 */

就是说,它允许在模块里重定义 pr_fmt,从而在本模块的日志前面加上某些前缀信息。所以内核好多文件里,在文件开头的位置,一般都有 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 这样一句定义。

比如,查看内核启动日志:

smp: Bringing up secondary CPUs ...

它对应的代码是这样的:

FILE: kernel/smp.c

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...

pr_info("Bringing up secondary CPUs ...\n");

所以日志里的 "smp: " 前缀,是由 pr_fmt 定义的,这样可以进一步地简化并统一内核模块里的打印。

如果搜索内核代码,还会看到这样的定义,可以借鉴并使用到自己的代码里去:

#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
#define pr_fmt(fmt) "kasan test: %s " fmt, __func__
#define pr_fmt(fmt) "debug_vm_pgtable: [%-25s]: " fmt, __func__

dev_err() 等包装函数

Linux kernel coding style 里有一节 Printing kernel messages

There are a number of driver model diagnostic macros in <linux/device.h>
which you should use to make sure messages are matched to the right device
and driver, and are tagged with the right level:  dev_err(), dev_warn(),
dev_info(), and so forth.  For messages that aren't associated with a
particular device, <linux/printk.h> defines pr_notice(), pr_info(),
pr_warn(), pr_err(), etc.

这是说,对于设备驱动实现代码里,建议使用 dev_err()、dev_warn()、dev_info() 等等打印函数,它们会在 printk 的基础上,加上当前模块相关的信息,使得打印更加清晰和通用。这些包装函数包括:

  • KERN_EMERG: dev_emerg()
  • KERN_ALERT: dev_alert()
  • KERN_CRIT: dev_crit()
  • KERN_ERR: dev_err()
  • KERN_WARNING: dev_warn()
  • KERN_NOTICE: dev_notice()
  • KERN_INFO: dev_info()
  • KERN_DEBUG: dev_dbg()

这套函数的实现在 drivers/base/core.c 里,用 define_dev_printk_level 宏定义了函数的实现。

类似于 pr_fmt,这套函数也有 dev_fmt,驱动代码里可以重新定义。

使用的时候,可以这样:

dev_err(&pdev->dev, "no memory resource defined\n");

参考资料

  • Documentation/process/coding-style.rst
  • Documentation/core-api/printk-basics.rst

Read More: