在驱动开发的早期, printk 非常有助于调试和测试新代码. 当你正式发行驱动时, 换句 话说, 你应当去掉, 或者至少关闭, 这些打印语句. 不幸的是, 你很可能会发现, 就在你 认为你不再需要这些消息并去掉它们时, 你要在驱动中实现一个新特性(或者有人发现了 一个 bug), 你想要至少再打开一个消息. 有几个方法来解决这 2 个问题, 全局性地打开 或关闭你地调试消息和打开或关闭单个消息.
这里我们展示一种编码 printk 调用的方法, 你可以单独或全局地打开或关闭它们; 这个 技术依靠定义一个宏, 在你想使用它时就转变成一个 printk (或者 printf)调用.
- 每个 printk 语句可以打开或关闭, 通过去除或添加单个字符到宏定义的名子.
- 所有消息可以马上关闭, 通过在编译前改变 CFLAGS 变量的值.
- 同一个 print 语句可以在内核代码和用户级代码中使用, 因此对于格外的消息, 驱动和测试程序能以同样的方式被管理.
下面的代码片断实现了这些特性, 直接来自头文件 scull.h:
13 [13]
* 例如, 使用 setlevel 8; setconsole 10 来配置终端 10 来显示消息.
63
#undef PDEBUG /* undef it, just in case */
#ifdef SCULL_DEBUG
# ifdef KERNEL
/* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)
# else
/* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#undef PDEBUGG #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
符号 PDEBUG 定义和去定义, 取决于 SCULL_DEBUG 是否定义, 和以何种方式显示消息适 合代码运行的环境: 当它在内核中就使用内核调用 printk, 在用户空间运行就使用 libc 调用 fprintf 到标准错误输出. PDEBUGG 符号, 换句话说, 什么不作; 他可用来轻易地" 注释" print 语句, 而不用完全去掉它们.
为进一步简化过程, 添加下面的行到你的 makfile 里:
# Comment/uncomment the following line to disable/enable debugging DEBUG = y
# Add your debugging flag (or not) to CFLAGS ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines else
DEBFLAGS = -O2
endif
CFLAGS += $(DEBFLAGS)
本节中出现的宏定义依赖 gcc 对 ANSI C 预处理器的扩展, 支持带可变个数参数的宏定 义. 这个 gcc 依赖不应该是个问题, 因为无论如何内核固有的非常依赖于 gcc 特性. 另 外, makefile 依赖 GNU 版本的 make; 再一次, 内核也依赖 GNU make, 所以这个依赖不 是问题.
如果你熟悉 C 预处理器, 你可以扩展给定的定义来实现一个"调试级别"的概念, 定义不 同的级别, 安排一个整数(或者位掩码)值给每个级别, 以便决定它应当多么详细.
但是每个驱动有它自己的特性和监视需求. 好的编程技巧是在灵活性和效率之间选择最好 的平衡, 我们无法告诉你什么是最好的. 记住, 预处理器条件(连同代码中的常数表达式) 在编译时执行, 因此你必须重新编译来打开或改变消息. 一个可能的选择是使用 C 条件 句, 它在运行时执行, 因而, 能允许你在出现执行时打开或改变消息机制. 这是一个好的 特性, 但是它在每次代码执行时需要额外的处理, 这样即便消息给关闭了也会影响效率. 有时这个效率损失无法接受.
64
本节出现的宏定义已经证明在多种情况下是有用的, 唯一的缺点是要求在任何对它的消息 改变后重新编译.