Chinaunix首页 | 论坛 | 博客
  • 博客访问: 185396
  • 博文数量: 87
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 926
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-24 17:14
文章分类

全部博文(87)

文章存档

2010年(87)

我的朋友

分类: LINUX

2010-07-26 19:09:54

在Linux中系统信息的显示有以下3种情况:
1、如果系统中只运行klogd,那么可以通过klogd -c 重新启动klogd并设置console_loglevel,然后小于console_loglevel的所有信息都会打印到控制台;
2、如果系统中只运行syslogd,那么消息不会传递到用户空间,只能通过读/proc/kmsg来察看消息;
3、如果系统中同时允许klogd和syslogd,那么在根据console_loglevel将消息筛选打印到控制台的前提下,任何消息都会被追加到文件/var/log/messages中。
一、内核中的调试支持
    在前面已经建议过:学习编写驱动程序要构建安装自己的内核(标准主线内核)。最重要的原因之一是:内核开发者已经建立了多项用于调试的功能。但是由于这些功能会造成额外的输出,并导致能效下降,因此发行版厂商通常会禁止发行版内核中的调试功能。为了实现内核调试,我在内核配置上增加了几项:
  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
书上介绍的还有其他配置,有的我不需要,或是s3c2440不支持,菜单里看不见。

二、通过打印调试
(1)printk
   printk与普通printf语句的差别之一就是,printk根据附加的不同的日志级别(loglevel),或者说是消息级别,来表示的严重程度对消息进行分类。比如:printk(KERN_DEBUG “Hi, My printk!”);上述语句所要输出的信息就是调试级别的。需要注意的是KERN_DEBUG和后面引号中的内容之间不能用逗号隔开,否则编译时会报错! 
   Linux在头文件中定义了8种可用的日志级别字符串,根据严重程度排序如下:

#define    KERN_EMERG    "<0>"    /* system is unusable           */
#define    KERN_ALERT    "<1>"   /* action must be taken immediately*/
#define    KERN_CRIT    "<2>"    /* critical conditions    */
#define    KERN_ERR    "<3>"    /* error conditions            */
#define    KERN_WARNING    "<4>"    /* warning conditions   */
#define    KERN_NOTICE    "<5>"    /* normal but significant condition */
#define    KERN_INFO    "<6>"    /* informational            */
#define    KERN_DEBUG    "<7>"    /* debug-level messages   */


未指定日志级别的printk语句采用默认级别,也就是在kernel/printk.c中被指定的宏DEFAULT_MESSAGE_LOGLEVEL,这个宏通常就是KERN_WARNING。

#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */


   
当优先级的值小于console_loglevel这个整数变量的值(也就是说优先级高于console_loglevel时,因为这个值是越小,优先级越高),信息才能显示出来。而console_loglevel的初始值DEFAULT_CONSOLE_LOGLEVEL也定义在/kernel/printk.c中:

#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */


   因此,我们需要事先将系统的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下面的每个文件都绑定于一个内核函数,用户读取其中的文件时,该函数动态的生成文件的内容。如:

[root@FriendlyARM kernel]# cat /proc/devices                                            
Character devices:                                                                      
  1 mem                                                                                 
  4 /dev/vc/0                                                                           
  4 tty                                                                                 
  5 /dev/tty                                                                            
  5 /dev/console                                                                        
  5 /dev/ptmx                                                                           
  7 vcs                                                                                 
 10 misc                                                                                
 13 input                                                                               
 14 sound                                                                               
 29 fb                                                                                  
 81 video4linux                                                                         
 89 i2c                                                                                 
 90 mtd                                                                                 
116 alsa                                                                                
128 ptm                                                                                 
136 pts                                                                                 
180 usb                                                                                 
188 ttyUSB                                                                              
189 usb_device                                                                          
204 s3c2410_serial                                                                      
253 usb_endpoint                                                                        
254 rtc                                                                                 
                                                                                        
Block devices:                                                                          
259 blkext                                                                              
  7 loop                                                                                
  8 sd                                                                                  
 31 mtdblock                                                                            
 65 sd                                                                                  
 66 sd                                                                                  
 67 sd                                                                                  
 68 sd                                                                                  
 69 sd                                                                                  
 70 sd                                                                                  
 71 sd                                                                                  
128 sd                                                                                  
129 sd                                                                                  
130 sd                                                                                  
131 sd                                                                                  
132 sd                                                                                  
133 sd                                                                                  
134 sd                                                                                  
135 sd                                                                                  
179 mmc   


使用/proc的模块必须包含,而使用seq_file接口要包含
具体的应用方法看源程序、做实验更有效果。


    至于其他的调试方法,如gdb、LTT、SysRq等方法,在其他的书籍,如:《嵌入式Linux系统开发技术详解-基于ARM》、《构建嵌入式Linux系统》等,上讲解的更为详细,以后专门花时间研究。

四、源码实验
模块程序链接:模块程序
模块测试程序链接模块测试程序

实验现象:

[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod scull_debug.ko scull_nr_devs=1 scull_quantum=6 scull_qset=2
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./scull_test
write code=6
write code=6
write code=6
write code=2
read code=6
read code=6
read code=6
read code=2
[0]=0 [1]=1 [2]=2 [3]=3 [4]=4
[5]=5 [6]=6 [7]=7 [8]=8 [9]=9
[10]=10 [11]=11 [12]=12 [13]=13 [14]=14
[15]=15 [16]=16 [17]=17 [18]=18 [19]=19

[Tekkaman2440@SBC2440V4]#cd /proc/
[Tekkaman2440@SBC2440V4]#ls
1              751            cmdline        kallsyms       stat
2              769            cpu            kmsg           swaps
3              77             cpuinfo        loadavg        sys
4              778            crypto         locks          sysrq-trigger
5              779            devices        meminfo        sysvipc
59             78             diskstats      misc           timer_list
6              781            driver         modules        tty
60             783            execdomains    mounts         uptime
63             785            filesystems    mtd            version
65             79             fs             net            vmstat
707            80             ide            partitions     yaffs
708            819            interrupts     scullmem       zoneinfo
709            asound         iomem          scullseq
710            buddyinfo      ioports        self
742            bus            irq            slabinfo


[Tekkaman2440@SBC2440V4]#cat scullmem

Device 0: qset 2, q 6, sz 20
  item at c071ebd4, qset at c071ef7c
  item at c071ef14, qset at c071eee0
       0: c071eeac
       1: c071ee78
[Tekkaman2440@SBC2440V4]#cat scullseq

Device 0: qset 2, q 6, sz 20
  item at c071ebd4, qset at c071ef7c
  item at c071ef14, qset at c071eee0
       0: c071eeac
       1: c071ee78
[Tekkaman2440@SBC2440V4]#rmmod scull_debug
[Tekkaman2440@SBC2440V4]#ls
1              742            buddyinfo      iomem          self
2              751            bus            ioports        slabinfo
3              769            cmdline        irq            stat
4              77             cpu            kallsyms       swaps
5              778            cpuinfo        kmsg           sys
59             779            crypto         loadavg        sysrq-trigger
6              78             devices        locks          sysvipc
60             781            diskstats      meminfo        timer_list
63             783            driver         misc           tty
65             785            execdomains    modules        uptime
707            79             filesystems    mounts         version
708            80             fs             mtd            vmstat
709            824            ide            net            yaffs
710            asound         interrupts     partitions     zoneinfo

阅读(982) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-07-30 22:30:56

哈哈,挻有意思的,可惜看不懂!