很多内核开发者喜欢的调试工具是printk,在Linux内核中,使用printk()函数来打印信息,它与C库的printf()函数类似。
printk()与printf()的一个重要区别是:printk()提供输出等级。内核会根据这个等级来判断是否在终端或者串口中输出。
printk等级
路径:include/linux/kern_levels.h
#define KERN_EMERG KERN_SOH "0" /* 最高输出等级,系统可能处于不可用的状态 */ #define KERN_ALERT KERN_SOH "1" /* 紧急和理科需要处理的输出 */ #define KERN_CRIT KERN_SOH "2" /* 紧急情况 */ #define KERN_ERR KERN_SOH "3" /* 发生错误的情况 */ #define KERN_WARNING KERN_SOH "4" /* 警告 */ #define KERN_NOTICE KERN_SOH "5" /* 重要的提示 */ #define KERN_INFO KERN_SOH "6" /* 提示信息 */ #define KERN_DEBUG KERN_SOH "7" /* 调试输出 */
Linux内核为printk定义了8个输出等级,KERN_EMERG
等级最高,KERN_DEBUG
等级最低。在配置内核时,由一个宏来设置系统默认的输出等级CONFIG_MESSAGE_LOGLEVEL_DEFAULT
,通常这个默认输出等级为4
,因此只有输出等级高于4时才会输出到终端或者串口,即只有KERN_EMERG~KERN_ERR满足这个条件。
通常在产品开发阶段,会把系统默认等级设置为最低,以便在开发测试阶段可以暴露更多的问题和调试信息,在发布产品时再把输出等级设置为0或者4。
修改printk等级
# cat /proc/sys/kernel/printk //printk默认有4个等级 7 4 1 7
四个数字分表代表:
控制台输出等级
默认消息等级
最低输出等级
默认控制台输出等级
在系统运行时,我们也可以修改系统的输出等级。打开所有的内核输出:
echo 8 > /proc/sys/kernel/printk //打开所有的内核输出
另外,还可以通过在启动内核时传递commandline
给内核的方法来修改系统默认的输出等级。例如,使用uboot引导内核时,可以在uboot传参的bootargs
参数上,加上“loglevel=8
”,这样在系统启动时,就打开了所有内核输出。
printk的输出格式
在实际调试中,printk()
可以和printf()
一样,直接输出一条字符串。
不过为了更好的显示一些调试信息,可以加上函数名字(__func__)
和代码行号(__LINE__)
,例如:
printk(KERN_EMERG"figo:%s, %d", __func__, __LINE__);
在双引号""前加上输出等级KERN_EMERG
,代表输出等级为0
。
另外,在使用printk()的时候需要注意输出格式,否则在编译时会出现很多的警告。printk的输出格式:
数据类型 | printk格式符 |
int | %d或%x |
unsigned int | %u或%x |
long | %ld或%lx |
long long | %lld或%llx |
unsigned long long | %llu或%llx |
size_t | %zu或%zx |
size_t | %zd或%zx |
函数指针 | %pf |
pr_xx( )封装
在使用printk的时候需要手动添加输出等级KERN_INFO、KERN_WARNING等,这样还是有些麻烦。因此,Linux内核也对printk进行了进一步的封装。
Linux内核将每一个输出等级封装为pr_xx()函数,例如,输出等级KERN_INFO
封装为pr_info(),输出等级KERN_WARNING
封装为pr_warn()。具体如下:
#define pr_emerg(fmt, ...) \ printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) #define pr_alert(fmt, ...) \ printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) #define pr_crit(fmt, ...) \ printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) #define pr_err(fmt, ...) \ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) #define pr_warn(fmt, ...) \ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) #define pr_notice(fmt, ...) \ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) #define pr_err(fmt, ...) \ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
这里对输出等级为KERN_DEBUG
的封装是比较特殊的,因为debug等级比较常用,内核对pr_debug()分为了三种情况:
如果设置了 CONFIG_DYNAMIC_DEBUG
,则此pr_debug()扩展为 dynamic_pr_debug(),主要用于动态输出。否则,如果定义了 DEBUG
宏,则它等同于具有 KERN_DEBUG
日志级别的 printk。如果未定义 DEBUG,则它什么都不做。
pr_debug()的定义如下:
/* If you are writing a driver, please use dev_dbg instead */ #if defined(CONFIG_DYNAMIC_DEBUG) || \ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) #include <linux/dynamic_debug.h> /** * pr_debug - Print a debug-level message conditionally * @fmt: format string * @...: arguments for the format string * * This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is * set. Otherwise, if DEBUG is defined, it's equivalent to a printk with * KERN_DEBUG loglevel. If DEBUG is not defined it does nothing. * * It uses pr_fmt() to generate the format string (dynamic_pr_debug() uses * pr_fmt() internally). */ #define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__) #elif defined(DEBUG) #define pr_debug(fmt, ...) \ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif
end
猜你喜欢:
RISC-V SiFive U64内核——HPM硬件性能监视器
RISC-V SiFive U64内核——L2 Prefetcher预取器
RISC-V SiFive U54内核——PMP物理内存保护
RISC-V SiFive U54内核——PLIC平台级中断控制器