内核编程八:基于printk宏的pr_* 宏

[复制链接]
发表于 2025-10-21 00:55:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
pr_* 宏

在之前的文章讲到printk的使用方法,我们发现通过printk宏打各个级别的日记非常繁琐,以是在Linux内核中基于printk宏又新界说了一些宏,比方在 include/linux/printk.h 中的如下界说:
  1. #define pr_emerg(fmt, ...)    printk(KERN_EMERG fmt, ##__VA_ARGS__)
  2. #define pr_alert(fmt, ...)    printk(KERN_ALERT fmt, ##__VA_ARGS__)
  3. #define pr_crit(fmt, ...)     printk(KERN_CRIT fmt, ##__VA_ARGS__)
  4. #define pr_err(fmt, ...)      printk(KERN_ERR fmt, ##__VA_ARGS__)
  5. #define pr_warn(fmt, ...)     printk(KERN_WARNING fmt, ##__VA_ARGS__)
  6. #define pr_notice(fmt, ...)   printk(KERN_NOTICE fmt, ##__VA_ARGS__)
  7. #define pr_info(fmt, ...)     printk(KERN_INFO fmt, ##__VA_ARGS__)
复制代码
比方:
  1. pr_err("Disk error occurred!\n");
复制代码
等价于:
  1. printk(KERN_ERR "Disk error occurred!\n");
复制代码
pr_debug 与 pr_devel 


  •         pr_debug:
    1. #ifdef DEBUG
    2. #define pr_debug(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
    3. #else
    4. #define pr_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
    5. #endif
    复制代码

    • 只有在 DEBUG 选项开启 (#define DEBUG) 时才会打印日记。
    • 否则会被优化为空(no_printk),不影响性能
           
  •         pr_devel:
    1. #ifdef CONFIG_DYNAMIC_DEBUG #define pr_devel(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
    2. #else
    3. #define pr_devel(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
    4. #endif
    复制代码

    • 作用雷同 pr_debug,但受 CONFIG_DYNAMIC_DEBUG 选项控制,实用于动态调试日记。
           
printk 和 pr_* 宏的关系


  • printk 是内核日记的底层实现,直接用于输出日记信息,必要手动指定 KERN_* 级别。
  • pr_* 宏是 printk 的封装,使代码更简便、可读性更强,不必要手动添加 KERN_* 级别。
  • pr_debug 和 pr_devel 只有在特定编译选项启用时才会收效,实用于调试阶段。
代码示例
  1. static int __init my_init(void)
  2. {
  3.     printk(KERN_INFO "This is a test message using printk\n");
  4.     pr_info("This is a test message using pr_info\n");
  5.     return 0;
  6. }
复制代码
这两种方式终极的结果是一样的,但 pr_info 更简便,保举使用 pr_* 宏。
printk VS pr_*

方法作用需手动加 KERN_*是否保举printk直接打印日记是否(代码冗长)pr_*更简便的 printk 封装否是pr_debug仅在 DEBUG 选项启用时收效否仅用于调试pr_devel仅在 CONFIG_DYNAMIC_DEBUG 启用时收效否仅用于动态调试在 Linux 内核开发中,保举使用 pr_* 宏来替换 printk,以进步代码可读性和维护性。

宏中的参数详解

  1. #define pr_emerg(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__)
复制代码
这个宏界说中,参数部门 fmt, ... 以及 ##__VA_ARGS__ 的作用如下:
参数部门
  1. #define pr_emerg(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__)
复制代码

  • fmt:格式化字符串,雷同 printf 中的格式字符串,比方 "System is down!\n"。
  • ...(可变参数):体现零个或多个额外参数,用于格式化 fmt 中的占位符,如 %d、%s 等。
示例:
  1. pr_emerg("System failure at %d!\n", 42);
复制代码
睁开后酿成:
  1. printk(KERN_EMERG "System failure at %d!\n", 42);
复制代码
##__VA_ARGS__ 作用

##__VA_ARGS__ 是 GCC 预处置惩罚器 提供的特殊语法,它的作用是 处置惩罚变参为空的情况,克制语法错误。
如果 pr_emerg 被调用时没有可变参数,比方:
  1. pr_emerg("System is down!\n");
复制代码
直接睁开为:
  1. printk(KERN_EMERG "System is down!\n");
复制代码
由于 __VA_ARGS__ 为空,## 会移除前面多余的 ,,包管不会出现 printk(KERN_EMERG "System is down!\n", ); 这种错误。
如果 ##__VA_ARGS__ 不存在,调用 pr_emerg("Hello") 大概会睁开成:
  1. printk(KERN_EMERG "Hello", );
复制代码
如许会导致编译错误。
##__VA_ARGS__ 实用场景


  •         有变参 时,正常睁开:
    1. pr_emerg("Error code: %d\n", 404);
    复制代码
    睁开为:
    1. printk(KERN_EMERG "Error code: %d\n", 404);
    复制代码
  •         无变参 时,去掉多余逗号:
    1. pr_emerg("Critical failure!\n");
    复制代码
    睁开为:
    1. printk(KERN_EMERG "Critical failure!\n");
    复制代码
代码示例
  1. #define pr_emerg(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__)
  2. void test() {
  3.     pr_emerg("System halted\n");         // 无变参
  4.     pr_emerg("Error code: %d\n", 500);   // 有变参
  5. }
复制代码
睁开后
  1. void test() {
  2.     printk(KERN_EMERG "System halted\n");         
  3.     printk(KERN_EMERG "Error code: %d\n", 500);   
  4. }
复制代码

小结

语法作用fmt必须参数,格式字符串...可选参数,支持多个变参__VA_ARGS__代表全部通报的变参##__VA_ARGS__处置惩罚无变参时自动去掉前面多余的 ,,防止编译错误这使 pr_emerg 兼容 带参数无参数 两种情况,进步了 printk 的机动性和安全性。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表