Linux内核API完全参考手册(第2版)
上QQ阅读APP看书,第一时间看更新

1.3 内核调试函数printk

本书中的模块代码用到了内核调试函数printk( ),在用户空间里我们经常使用C语言函数printf( )来向标准输出终端打印信息。printk( )是内核使用的函数,因为内核没有链接到标准C函数库,其实printk( )接口同printf( )基本相似,printk( )函数能够在终端一次最多显示大小为1024字节的字符串。printk( )函数执行时首先设法获取控制台信号量,然后将要输出的字符存储到控制台的日志缓冲区,再调用控制台驱动程序来刷新缓冲区。若printk( )无法获得控制台信号量,就只能把要输出的字符存储到日志缓冲区,并依赖拥有控制台信号量的进程来刷新这个缓冲区。printk( )函数会将数据存储到日志缓冲区,但是为了安全考虑在这之前需要使用日志缓冲区锁,保证并发调用printk( )的安全性。内核态系统信息输出函数printk( )与用户态下的printf( )函数在输出内容上也是有区别的,主要有两点:内核在切换模式时不保存处理器的浮点状态,因此printk( )并不支持浮点数运算;printk( )可以指定一个记录级别,内核根据这个级别来判断是否在终端上打印消息,而printf( )函数则不需要。

printk( )的语法格式为:

        printk(记录级别 “格式化输出信息”);

或者是采用编号形式:

        printk(“<记录级别编号>格式化输出信息”);

其中“记录级别”是include/linux/kern_levels.h中的简单宏定义,其在内核源代码中的形式如图1-2所示的第7~16行。它们扩展后是如“<number>”这样的字符串,加入printk( )函数要打印的消息的前面。

图1-2 记录级别在内核源代码中的宏定义

内核用这个指定的记录等级和当前终端的记录等级console_loglevel进行比较,从而决定是不是向终端打印输出。表1-1给出了所有记录等级、字符串代号及说明。

表1-1 记录等级说明

内核将最重要的记录等级KERN_EMERG定为“<0>”,将无关紧要的记录等级KERN_DEBUG定为“<7>”。

例如:

        printk(“没有等级信息,则采用默认级别!\n”);
        printk(KERN_INFO “内核提示信息\n”);
        printk(KERN_DEBUG “内核调试信息\n”);

如果没有特别指定一个记录等级,函数会选用默认的DEFAULT_MESSAGE_LOGLEVEL,现在默认等级是KERN_WARNING。由于这个默认值将来存在变化的可能性,所以还是应该给消息指定一个记录等级。

本章后面的实例中考虑到使系统强制输出信息,所以我们使用了“<0>”级别,然后通过dmesg | tail或者是dmesg -c来查看系统输出信息。