Chinaunix首页 | 论坛 | 博客
  • 博客访问: 397245
  • 博文数量: 115
  • 博客积分: 2501
  • 博客等级: 少校
  • 技术积分: 1009
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-23 17:05
文章分类

全部博文(115)

文章存档

2011年(2)

2010年(86)

2009年(27)

我的朋友

分类: 嵌入式

2010-08-10 22:32:13

 

四、打开和关闭消息

在驱动开发的早期, printk 非常有助于调试和测试新代码. 当你正式发行驱动时, 换句话说, 你应当去掉, 或者至少关闭, 这些打印语句.

通过在编译前改变 CFLAGS 变量的值,所有消息可以马上关闭,同一个 print 语句可以在内核代码和用户级代码中使用, 因此对于格外的消息, 驱动和测试程序能以同样的方式被管理.

下面的代码片断实现了这些特性, 直接来自头文件 scull.h:

下面是我的实验:

test.c

#include

#include

 

 

#ifdef MY_DEBUG

/* This one for user space */

# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)

#else

# define PDEBUG(fmt, args...) /* not debugging: nothing */

#endif

 

int main()

{

    PDEBUG("%s,test the printf switch\n","hello");

    exit(0);

}

 

Makefile中未定义MY_DEBUG宏,即关闭调试信息。

Makefile

OBJS = test

CROSS_COMPILE =

CC = $(CROSS_COMPILE)gcc

 

.PHONY:all

all:

        $(CC) -o $(OBJS) $(OBJS).c

        #$(CC) -o $(OBJS) $(OBJS).c -O -DSCULL_DEBUG

 

.PHONY:clean

clean:

        rm -rf *~ *.o $(OBJS)

 

这时将看不到打印信息

root@ubuntu:/samba_share/test# ./test

root@ubuntu:/samba_share/test#

 

 

Makefile中定义MY_DEBUG宏时,将打开调试信息。

Makefile

OBJS = test

CROSS_COMPILE =

CC = $(CROSS_COMPILE)gcc

 

.PHONY:all

all:

        #$(CC) -o $(OBJS) $(OBJS).c

        $(CC) -o $(OBJS) $(OBJS).c -O -DMY_DEBUG

 

.PHONY:clean

clean:

        rm -rf *~ *.o $(OBJS)

运行test后看到打印信息

root@ubuntu:/samba_share/test# ./test

hello,test the printf switch

除了在Makefile中编译时定义一些宏外,还可以在源码中定义一些宏开关,来开启或关闭监视信息。

 

五、速率限制

有时为避免终端打印大量的信息,系统运行变慢,其它程序无法得到及时响应,往往找不出什么原因,一屏蔽掉大量的打印信息后,其它部分工作正常了。这在我的工作中遇到一次,CPU与外设通过SPI通信,SPI速率为1Mbps多,大量的串口终端打印信息导致CPU与外设通信不正常,关闭打印信息后一切正常。

Linux内核提供了一个防止重复打印相同信息的函数:

int printk_ratelimit(void);

典型的调用如下所示:

if(printk_ratelimit())

       printk(“KERN_NOTICE “The printer is still on fire\n”);

printk_ratelimit 的行为可以通过修改 /proc/sys/kern/printk_ratelimit( 在重新打开消息前等待的秒数 ) /proc/sys/kernel/printk_ratelimit_burst(限速前可接收的消息数)来定制.

 

六、打印设备编号

内核提供了一些实用的宏定义( 中定义)用于这个目的:

int print_dev_t(char *buffer, dev_t dev);

char *format_dev_t(char *buffer, dev_t dev);

两个宏定义都将设备号编码进给定的缓冲区,唯一的区别是 print_dev_t 返回打印的字符数, format_dev_t 返回缓存区。

 

七、通过查询调试

Syslogd将每件事情都记录到磁盘上,以便系统崩溃后还能查看崩溃前的状况。但它也会降低系统的性能。一种提高系统性能的方法就是不适用log,而是在需要查看系统信息时用工具来查询,如pscatnetstat等。

 

八、使用/proc文件系统

/proc文件系统是一个特殊的软件创建的文件系统, 内核用来输出消息到外界。大部分时间, /proc 条目是只读的文件。

要创建一个只读 /proc 文件, 你的驱动必须实现一个函数来在文件被读时产生数据. 当某个进程读文件时(使用 read 系统调用),  这个请求通过这个函数到达你的模块。当一个进程读你的 /proc 文件, 内核分配了一页内存(就是说, PAGE_SIZE 字节,一个PAGE_SIZE4KB), 驱动可以写入数据来返回给用户空间. 那个缓存区传递给你的函数, 是一个称为 read_proc 的方法:

int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);

page 指针是你写你的数据的缓存区; start 是这个函数用来说有关的数据写在页中哪里(下面更多关于这个); offset count 对于 read 方法有同样的含义. eof 参数指向一个整数, 必须由驱动设置来指示它不再有数据返回, data 是驱动特定的数据指针, 你可以用做内部用途.

       创建proc文件时通过如下函数:

struct proc_dir_entry *create_proc_read_entry(const char *name,mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data);

这里, name 是要创建的文件名子, mod 是文件的保护掩码(缺省系统范围时可以作为 0 传递), base 指出要创建的文件的目录( 如果 base NULL, 文件在 /proc 根下创建 ), read_proc 是实现文件的 read_proc 函数, data 被内核忽略( 但是传递给 read_proc). 下面是scull中创建/proc/scullmem的函数:

create_proc_read_entry("scullmem", 0 /* default mode */,

                       NULL /* parent dir */, scull_read_procmem,

                       NULL /* client data */);

这里,创建了一个名为 scullmem 的文件, 直接在 /proc , 带有缺省的, 全局可读的保护。

       proc文件的删除时通过如下函数:

remove_proc_entry("scullmem", NULL /* parent dir */);

       由以上可以看到使用create_proc_read_entry等函数创建和删除proc文件很简单,但如果是所要显示到proc文件中的内容很少的话,那将会浪费很多内存。

 

九、通过监视调试

有时小问题可以通过观察用户空间的应用程序的行为来追踪,你可以运行一个调试器来单步过它的函数, 增加打印语句, 或者在 strace 下运行程序。strace 命令时一个有力工具, 显示所有的用户空间程序发出的系统调用. 它不仅显示调用, 还以符号形式显示调用的参数和返回值。当一个系统调用失败, 错误的符号值(例如, ENOMEM)和对应的字串(Out of memory) 都显示。strace 从内核自身获取信息. 这意味着可以跟踪一个程序, 不管它是否带有调试支持编译( gcc -g 选项)以及不管它是否 strip . 你也可以连接追踪到一个运行中的进程, 类似于一个调试器的方式连接到一个运行中的进程并控制它。strace的使用我会作为一个单独的一部分来学习并在以后发表日记。

 

十、调试系统故障

当内核代码出错, 一个提示性的消息打印在控制台上。处理器使用的任何地址几乎都是一个虚拟地址, 通过一个复杂的页表结构映射为物理地址(例外是内存管理子系统自己使用的物理地址). 当引用一个无效的指针, 分页机制无法映射指针到一个物理地址, 处理器发出一个页错误给操作系统. 如果地址无效, 内核无法"换入"缺失的页面;这时,如果处理器恰好处于超级用户模式,系统就会产生一个oops

       一个oops消息已经能提示我们出现故障的位置,最相关的信息时指令指针(EIP),即出错指针的地址。另外,oops信息中的调用栈可以显示系统是如何到达故障点的,这个没有分析过。

 

十一 调试器和相关工具

kgdb可以用于调试内核,高版本的内核中已经支持kgdb了,这部分以后再单独学习。

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