当前位置: 首页 > news >正文

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

pr_*

在之前的文章讲到printk的使用方法,我们发现通过printk宏打各个级别的日志非常繁琐,所以在Linux内核中基于printk宏又新定义了一些宏,例如在 include/linux/printk.h 中的如下定义:

#define pr_emerg(fmt, ...)    printk(KERN_EMERG fmt, ##__VA_ARGS__)
#define pr_alert(fmt, ...)    printk(KERN_ALERT fmt, ##__VA_ARGS__)
#define pr_crit(fmt, ...)     printk(KERN_CRIT fmt, ##__VA_ARGS__)
#define pr_err(fmt, ...)      printk(KERN_ERR fmt, ##__VA_ARGS__)
#define pr_warn(fmt, ...)     printk(KERN_WARNING fmt, ##__VA_ARGS__)
#define pr_notice(fmt, ...)   printk(KERN_NOTICE fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...)     printk(KERN_INFO fmt, ##__VA_ARGS__)

例如:

pr_err("Disk error occurred!\n"); 

等价于:

printk(KERN_ERR "Disk error occurred!\n"); 

pr_debugpr_devel 

  • pr_debug:

    #ifdef DEBUG 
    #define pr_debug(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) 
    #else 
    #define pr_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) 
    #endif 
    • 只有在 DEBUG 选项开启 (#define DEBUG) 时才会打印日志。
    • 否则会被优化为空(no_printk),不影响性能。
  • pr_devel:

    #ifdef CONFIG_DYNAMIC_DEBUG #define pr_devel(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) 
    #else 
    #define pr_devel(fmt, ...) no_printk(fmt, ##__VA_ARGS__) 
    #endif 
    • 作用类似 pr_debug,但受 CONFIG_DYNAMIC_DEBUG 选项控制,适用于动态调试日志。

printkpr_* 宏的关系

  • printk 是内核日志的底层实现,直接用于输出日志信息,需要手动指定 KERN_* 级别。
  • pr_* 宏是 printk 的封装,使代码更简洁、可读性更强,不需要手动添加 KERN_* 级别。
  • pr_debugpr_devel 只有在特定编译选项启用时才会生效,适用于调试阶段。

代码示例

static int __init my_init(void)
{
    printk(KERN_INFO "This is a test message using printk\n");
    pr_info("This is a test message using pr_info\n");
    return 0;
}

这两种方式最终的效果是一样的,但 pr_info 更简洁,推荐使用 pr_* 宏。

printk VS pr_*

方法作用需手动加 KERN_*是否推荐
printk直接打印日志否(代码冗长)
pr_*更简洁的 printk 封装
pr_debug仅在 DEBUG 选项启用时生效仅用于调试
pr_devel仅在 CONFIG_DYNAMIC_DEBUG 启用时生效仅用于动态调试

在 Linux 内核开发中,推荐使用 pr_* 宏来替代 printk,以提高代码可读性和维护性。

宏中的参数详解

#define pr_emerg(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__)

这个宏定义中,参数部分 fmt, ... 以及 ##__VA_ARGS__ 的作用如下:

参数部分

#define pr_emerg(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__) 
  • fmt:格式化字符串,类似 printf 中的格式字符串,例如 "System is down!\n"
  • ...(可变参数):表示零个或多个额外参数,用于格式化 fmt 中的占位符,如 %d%s 等。

示例:

pr_emerg("System failure at %d!\n", 42); 

展开后变成:

printk(KERN_EMERG "System failure at %d!\n", 42); 

##__VA_ARGS__ 作用

##__VA_ARGS__GCC 预处理器 提供的特殊语法,它的作用是 处理变参为空的情况,避免语法错误。

如果 pr_emerg 被调用时没有可变参数,例如:

pr_emerg("System is down!\n"); 

直接展开为:

printk(KERN_EMERG "System is down!\n"); 

由于 __VA_ARGS__ 为空,## 会移除前面多余的 ,,保证不会出现 printk(KERN_EMERG "System is down!\n", ); 这种错误。

如果 ##__VA_ARGS__ 不存在,调用 pr_emerg("Hello") 可能会展开成:

printk(KERN_EMERG "Hello", ); 

这样会导致编译错误。

##__VA_ARGS__ 适用场景

  • 有变参 时,正常展开:

    pr_emerg("Error code: %d\n", 404); 

    展开为:

    printk(KERN_EMERG "Error code: %d\n", 404); 
  • 无变参 时,去掉多余逗号:

    pr_emerg("Critical failure!\n"); 

    展开为:

    printk(KERN_EMERG "Critical failure!\n"); 

代码示例

#define pr_emerg(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__)

void test() {
    pr_emerg("System halted\n");         // 无变参
    pr_emerg("Error code: %d\n", 500);   // 有变参
}

展开后

void test() {
    printk(KERN_EMERG "System halted\n");         
    printk(KERN_EMERG "Error code: %d\n", 500);   
}

小结

语法作用
fmt必须参数,格式字符串
...可选参数,支持多个变参
__VA_ARGS__代表所有传递的变参
##__VA_ARGS__处理无变参时自动去掉前面多余的 ,,防止编译错误

这使 pr_emerg 兼容 带参数无参数 两种情况,提高了 printk 的灵活性和安全性。

相关文章:

  • 网络安全中蓝牙攻击有哪些?
  • EasyDSS视频推拉流系统:清理缓存文件时如何确保缓存读写不受影响?
  • 【无标题】FrmImport
  • 最短路算法 dijkstra 从认识到熟练掌握
  • 爬虫:从Chrome浏览器进行抓包详解
  • 【AD】PCB增加相关图层——以机械层为例
  • DAViMNet:基于状态空间模型的域自适应目标检测
  • 【单片机】嵌入式系统的硬件与软件特性
  • python代码注释方式
  • Apache Tomcat 新手入门指南:从安装到部署的全流程解析
  • 深入解析 C# 中的泛型:概念、用法与最佳实践
  • Win11存储空间掉盘修复,正确移除不存在的硬盘
  • Python如何制作并查询sql数据库
  • 2000-2020年各省社会消费品零售总额数据
  • kotlin的val声明的变量是常量吗
  • 深入理解 FreeRTOS 的中断管理:屏蔽机制、临界区与实验分析
  • MySQL的底层原理与架构
  • 【HeadFirst系列之HeadFirst设计模式】第14天之与设计模式相处:真实世界中的设计模式
  • 如何在DEV community上发表blog?
  • MySQL压缩版安装详细图解
  • 空调维修网站模板/疫情最新动态
  • wordpress 开启评论/广州seo网站优化培训
  • 网站开发要什么样的环境/做网站多少钱
  • 三明企业网站建设/免费网站推广网站破解版
  • 网站必须天天更新吗/亚马逊查关键词搜索量的工具
  • 巴楚网站建设/店铺运营方案策划