在前面已经建议过:学习编写驱动程序要构建安装自己的内核(标准主线内核)。最重要的原因之一是:内核开发者已经建立了多项用于调试的功能。但是由于这些功能会造成额外的输出,并导致性能下降,因此发行版厂商通常会禁止发行版内核中的调试功能。
为了实现内核调试,我在内核配置上增加了几项:
Kernel hacking --->
[*] Magic SysRq key
[*] Kernel debugging
[*] Debug slab memory allocations
[*] Spinlock and rw-lock debugging: basic checks
[*] Spinlock debugging: sleep-inside-spinlock checking
[*] Compile the kernel with debug info
[*] Magic SysRq key
Device Drivers --->
Generic Driver Options --->
[*] Driver Core verbose debug messages
General setup --->
[*] Configure standard kernel features (for small systems) --->
[*] Load all symbols for debugging/ksymoops
等等。
二、通过打印调试
首先,printk有8个loglevel,定义在<linux/kernel.h>中:
|
所以printk()可以这样用:printk(KERN_INFO "Hello, world!\n");
未指定日志级别的printk( "Hello, world!\n") 采用的默认级别是DEFAULT_MESSAGE_LOGLEVEL,这个宏在kernel/printk.c中被定义为整数4,即对应KERN_WARNING。
|
在/proc/sys/kernel/printk 会显示4个数值(可由echo修改),如下图。
#cat /proc/sys/kernel/printk
#echo 8 > /proc/sys/kernel/printk //通过echo修改当前日志级别 为8
根据日志级别,内核可能会把消息打印到当前控制台上,这个控制台可以是一个字符模式的终端、一个串口打印机或一个并口打印机。当优先级小于console_loglevel这个整数变量的值,消息才能显示出来,而且每次输出一行(如果不以newline字符结尾,则不会输出)。
如果系统同时运行了klogd和syslogd,则无论console_logleve为何值,内核消息都会追加到/var/log/messages(用户空间)中。
如果klogd没有运行,这些消息就不会传递到用户空间,这时只能查看/proc/kmsg文件(使用dmesg命令可以轻松做到)。
变量console_loglevel的初始值DEFAULT_CONSOLE_LOGLEVEL也定义在/kernel/printk.c中:
|
2.1 而在运行是改变console_loglevel的程序《Linux设备驱动程序(第3版)》提供,如下setlevel.c:
|
#./setlevel 2 //设置日志级别,我测试没有设置成功???
2.2 重定向到控制台 的程序《Linux设备驱动程序(第3版)》提供,如下setconsole.c:
#include <stdio.h> |
#./setconsole tty0 //参数总是错误的,不知道如何设置???
三、开启及关闭消息
下面给出了一个调用printk的编码方法,它可个别或全局地开关printk语句。是否定义符号PDEBUG取决于是否定义了SCULL_DEBUG,并且,它能根据代码所运行的环境来选择合适的方式显示信息。
这些宏被证实是非常有用的,仅有的缺点是每次开启和关闭调试都要重新编译模块。
另一种方法:使用C条件语句,它在运行时执行,因此可以在程序运行期间打开或者关闭,但每次代码执行时系统都要进行额外的处理,甚至在禁用消息后仍然会影响性能,而有时这种性能损失是无法接收的。
|
Makefile中要添加的语句:
# 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 else DEBFLAGS = -O2 endif CFLAGS += $(DEBFLAGS) |
速度限制
为了避免printk重复输出过快而阻塞系统,内核使用以下函数跳过部分输出:
|
典型的应用如下:
|
可以通过修改/proc/sys/kernel/printk_ratelimit(重开信息前应等待的秒数)和/proc/sys/kernel/printk_ratelimit_burst(在速度限制前可接受的信息数)来定制printk_ratelimit的行为。
打印设备编号
Linux还提供了打印设备编号的宏(在<linux/kdev_t.h>中定义):
|
两个函数的唯一区别是:print_dev_t返回打印字符数,format_dev_t返回缓冲区指针。注意缓冲区char *buffer的大小应至少有20B。
三、通过查询调试
驱动开发人员可以用如下方法对系统进行查询:在/proc文件系统中创建文件、使用驱动程序的ioctl方法、以及通过sysfs导出属性等。
使用/proc文件系统
/proc文件系统是一种特殊的、由软件创建的文件系统,内核使用他向外界导出信息。/proc下面的每个文件都绑定于一个内核函数,用户读取其中的文件时,该函数动态的生成文件的“内容”。
如以前用过的:/proc/modules列出的是当前载入模块的列表。