LDD3学习笔记《三》第四章

来源:互联网 发布:知乎 cos 编辑:程序博客网 时间:2024/06/10 05:17

一、printk的用法
例:
  printk(KERN_ALERT "Hello,World!/n");

  printk的用法与printf的用法差不多,

上面的例子改为printf:

  printf("Hello,World!/n");

  两个的不同点在于:一、printk有个表示日志级别的参数,比如上面的
KERN_ALERT。二、printk必须有一个换行符(/n),否则不能打印出来。
  详细说明日志级别参数。
  为什么要多加一个日志级别参数呢?我个人的理解是:printk是内核提供的函
数,也就是说它一般是运行在内核态的。内核的运行要求稳定但不失提供必须的信
息给用户。试想如果在内核中有两个信息要传达给用户,一个提示:硬盘烂了,不
可用;另一个提示:Hello, World!一个是关于系统生死的信息,一个不痛不痒的信
息,我们更关心哪个信息?如果我们一样对待这样的信息,一样让他们一起被打印
给用户,那有时候重要的信息,我们不得不采取的行动我们不能在第一时间做出反
映,因为我们要找出这些特殊的信息,我们不得不在一大堆无用的信息里寻找。如
果我们能把信息分类,重要的信息直接打印在你面前,如Hello, World般不痛不痒
的信息,让它找一地方睡着,哪天想看看把它翻出来就是了。这里只是简单地说明
为何在增加日志级别,更复杂的东西我谈不来,但这样也足够理解为何需要了。
  内核提供了哪些日志级别?在头文件<linux/kernel.h>中定义了八个可用的日
志级别。

  这些可用的日志级别在我们调用printk显性地给出,如例子中的KERN_ALERT。
当然也可以这样用:printk("Hello, World!/n");这不是和printf没什么区别了
吗?有区别,因为如果没有给出日志级别参数,那么就采用默认的日志级别:
DEFAULT_MESSAGE_LOGLEVEL,在kernel/printk.c中定义。通过查看这些日志级别的
定义我们知道,它们就是宏定义,编译器在预处理阶段展开这些宏。例如
printk("Hello, World!/n");展开为:printk("<4>Hello, World!/n");
  有了这些日志级别,还不足以根据需要打印不同的信息到我们面前。还要行动
klogd和一个console_loglevel.klogd用于把信息传递到用户空间,
console_loglevel字面解释是控制台日志级别,用于选择哪些信息打印出来显现给
我们看。只有日志级别小于console_loglevel时,消息才会打印到当前控制台上。
(区别虚拟终端和控制台--简单地说我们在图形界面下开启的命令行终端便是一
个虚拟终端,我们用Ctrl+Alt+F?切换的就是控制台)不管怎么样,消息都不会打
印到虚拟终端。一般只打印到控制台。
二、通过命令行修改控制台日志级别显示不同级别的调试信息

我们可以修改控制台日志级别,书本也给出了三种修改方法:1、通过加-c选项重
新启动klogd,2、通过程序修改,并且提供了修改程序,3、通过文本文件修改。

这里我们只讲通过文本文件修改非常简单,只要一个命令就可以了:echo 8
>/proc/sys/kernel/printk。文本文件:/proc/sys/kernel/printk包含4个整数
值,分别是:当前的控制台日志级别,默认的日志级别,最小允许的日志级别,引
导时默认控制台日志级别。有时候我们的klogd启动了,printk的用法没有错,就
是不能在当前控制台上打印出消息,这时很可能与我们的控制台日志级别有关,我
们可以通过命令行直接修改当前控制台日志级别,把它的数值改大一点,这样就可
以显示大部分的日志级别消息。

  基本上理解了这些东西以后我们就理解了printk的用法,以及程序没错,却没
打印出消息来的原因了。书上提到的其他知识需要额外地资料,而书本却没有给,
所以我们只理解这些就足够了。

三、proc接口的实现。

我们可以通过proc导出我们的模块的信息。下面的每个文件绑定于一个内核函数,
当读取该文件时,写它绑定的函数会动态生成内容。在我的cdd实例中,我实现了
一个cdd_read_procmem函数,在proc下生成的文件是cddmem,用于生成关于cdd设
备的信息,包括每个设备的内存地址,数据区域数据的大小等。要使用/proc必须
包含<linux/proc_fs.h>.整个过程的步骤如下:
1、实现当我们读取/proc下的文件时用于生成数据的函数。原型为:

  cdd中实现为:


buf参数指向内核创建的用于存放读取的数据的缓冲区,start参数在实现小文件时
可以直接用NULL。在cdd的实现中这些参数大都不用处理。
2、绑定数据生成函数与在要proc下生成的文件。用到的函数是:

 cdd中的实现为:

当这个函数执行的时候就会在/proc下生成cddmem文件,当读取这个文件时,
cdd_read_procmem就会生成数据返回给我们。这个绑定函数在模块加载时执行。
3、当模块卸载时/proc中的入口项也应被删除,也就是模块加载时创建的cddmem,
这个工作由:remove_proc_entry("cddmem", NULL);完成。这个函数在模块卸载时
执行。

通过上面三个步骤就能实现了通过/proc输出关于设备驱动模块的信息。这样的方
法比较简单,输出的信息一般比较小。
实验结果:

附:
老大关于第四章的学习要点:
1. 理解printk的使用方法,并且熟悉7个消息级别的含义。

2. 怎样通过命令行,选择控制台显示特定级别的调试消息

3. 怎样用proc的接口,显示驱动中的一些信息,修改第三章的驱动作业,实现proc接口,显示自定义的调试信息。

4. 除以上内容以外的部分,可以概览,不要求深入理解。