因此,我们需要事先将系统的console_loglevel调到一个适当的数值,以便控制台正确显示我们所需要的信息。比如上面语句中的“Hi, My first printk!”,若不修改console_loglevel的值,就无法在控制终端上显示。
修改方法有两种:一是通过sys_syslog系统调用进行修改;二是用dmesg进行修改或者修改/proc/sys/kernel/printk文件中的第一行,比如dmesg -n 1(命令中的1就是要设置的console_loglevel值,其实上面所说的KERN_EMERG到KERN_DEBUG也就是从0到7的整数值)。其中前者是临时性的,每当系统重启后console_logleve就会回到默认状态,而后者却是永久性的,只要用dmesg修改了console_loglevel的值,那么以后它就一直是这个值了。
而在运行时改变console_loglevel的程序(《Linux设备驱动程序(第3版)》提供)如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define __LIBRARY__ /* _syscall3 and friends are only available through this */ #include <linux/unistd.h> /* define the system call, to override the library function */ _syscall3(int, syslog, int, type, char *, bufp, int, len);
int main(int argc, char **argv) { int level; if (argc==2)
{ level = atoi(argv[1]); /* the chosen console */ }
else
{ fprintf(stderr, "%s: need a single arg\n",argv[0]);
exit(1); } if (syslog(8,NULL,level) < 0)
{ fprintf(stderr,"%s: syslog(setlevel): %s\n", argv[0],strerror(errno)); exit(1); } exit(0); }
|
但是上述代码在Linux2.6.20内核编译通过不了,提示在编译时提示:expected declaration specifiers or ‘…’ before syslog。问题很显然出现在_syscall3上。程序中_syscall3(int, syslog, int, type, char *, bufp, int, len)这条语句的作用是告诉编译器,需要的系统调用是syslog,返回值是int,而后面的字符串则是syslog需要的3个参数及其类型。
而setlevel.c之所以编译通不过,是因为Linux Device Driver3的作者是在2.6.10的Linux内核中写的这个程序,但是2.6.20的Linux内核中却并没有定义_syscall3这个函数。于是只要在setlevel.c中调用_syscall3之前定义它的原型,那么setlevel.c就可以正常编译和使用了。增加代码后的setlevel.c如下:
#include
#include
#include
#include
#include /*需要加上这个头文件,否则编译不能通过*/
#define __syscall_return(type, res) \
do { \
if ((unsigned long)(res) >= (unsigned long)(-125)) { \
errno = -(res); \
res = -1; \
} \
return (type) (res); \
} while (0)
#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
"d" ((long)(arg3))); \
__syscall_return(type,__res); \
}
_syscall3(int,syslog, int,type, char *,bufp, int,len);
int main(int argc, char **argv)
{
int level;
if (argc==2) {
level = atoi(argv[1]); /* the chosen console */
} else {
fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1);
}
if (syslog(8,NULL,level) < 0) {
fprintf(stderr,"%s: syslog(setlevel): %s\n",
argv[0],strerror(errno));
exit(1);
}
exit(0);
}
最关键的“syslog(8,NULL,level)”
语句我不理解,没有找到相关资料。但是通过在mini2440上的实验表明:程序是ok的!我用Hello world模块做了实验,现象和书上的一致。
还有通过对/proc/sys/kernel/printk的访问来改变
console_loglevel的值:
[root@FriendlyARM 2.6.29.4-FriendlyARM]# echo 1 >/proc/sys/kernel/printk [root@FriendlyARM 2.6.29.4-FriendlyARM]# ls hello.ko modules.dep.bb [root@FriendlyARM 2.6.29.4-FriendlyARM]# insmod hello.ko [root@FriendlyARM 2.6.29.4-FriendlyARM]# rmmod hello [root@FriendlyARM 2.6.29.4-FriendlyARM]# echo 7 >/proc/sys/kernel/printk [root@FriendlyARM 2.6.29.4-FriendlyARM]# insmod hello.ko (0) Hello, apple ! param[0]: 1 param[1]: 2 param[2]: 3 param[3]: 4 param[4]: 4 param[5]: 1 param[6]: -1069724280 param[7]: -1069724280 [root@FriendlyARM 2.6.29.4-FriendlyARM]# rmmod hello Goodbye,apple ! Love Linux !Love ARM !Love my wife ! [root@FriendlyARM 2.6.29.4-FriendlyARM]#
|
四个数字的含义:当前的loglevel、默认loglevel、最小允许的loglevel、引导时的默认loglevel。
为了方便的打开和关闭调试信息,《Linux设备驱动程序(第3版)》提供以下源码:
/* Macros to help debugging */ #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 */
|
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 # "-O" is needed to expand inlines else DEBFLAGS = -O2 endif CFLAGS += $(DEBFLAGS)
|
为了避免printk重复输出过快而阻塞系统,内核使用以下函数跳过部分输出:
int printk_ratelimit(void);
|
典型的应用如下:
if (printk_ratelimit( )) printk(KERN_NOTICE "The printer is still on fire\n");
|
可以通过修改/proc/sys/kernel/printk_ratelimit(重开信息前应等待的秒数)和/proc/sys/kernel/printk_ratelimit_burst(在速度限制前可接受的信息数)来定制printk_ratelimit的行为。
Linux还提供了打印设备编号的宏(在中定义):
int print_dev_t(char *buffer, dev_t dev); char *format_dev_t(char *buffer, dev_t dev);
|
两个函数的唯一区别是:print_dev_t返回打印字符数,format_dev_t返回缓冲区指针。注意缓冲区char *buffer的大小应至少有20B。
三、通过查询调试
多数情况中,获取相关信息的最好方法是在需要的时候才去查询系统信息,而不是持续不断地产生数据。
使用/proc文件系统
/proc文件系统是一种特殊的、由软件创建的文件系统,内核使用他向外界导出信息。/proc下面的每个文件都绑定于一个内核函数,用户读取其中的文件时,该函数动态的生成文件的内容。如: