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