Chinaunix首页 | 论坛 | 博客
  • 博客访问: 113254
  • 博文数量: 23
  • 博客积分: 471
  • 博客等级: 一等列兵
  • 技术积分: 251
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-21 15:21
文章分类
文章存档

2017年(1)

2013年(2)

2011年(20)

分类: LINUX

2011-08-15 09:55:42

1.内核中的调试支持
  
   在内核配置菜单中有“kernel hacking”菜单选项,这些选项帮助用户检查很多错误,这里我列了一个表方便大家参考:
  
 
 
2.printk打印调试
   在应用程序中,我们也经常使用这种经典的打印调试技术。在内核中,printk用来完成相同的工作。
   printk与printf的一个不同就是,前者可以对消息进行分类,表示日志级别的宏会自动展成一个字符串,用到的级别有以下几种:
 
   KERN_EMERG:紧急事件,系统崩溃前提示的消息。
   KERN_ALERT: 立即采取动作的情况。
   KERN_CRIT:  涉及到严重的硬件或者软件操作失败。
   KERN_ERR:   用于报告错误状态,驱动程序中用于报告来自硬件的问题。
   KERN_WARNING: 不会造成系统严重问题的一般警告。
   KERN_NOTICE: 进行提示的正常情形。
   KERN_INFO:   提示性信息,驱动程序一般提示找到了硬件信息。
   KERN_DEBUG:   用于debug。
一般我们需要将这些调试信息打印到控制台上,而控制台本身有一个日志级别(默认为DEFAULT_CONSOLE_LOGLEVEL),printk制定的级别必须数值上小于该默认值且以newline结束才能打印到控制台上,所以我们有时候需要更改控制台的日志级别,比如下面可以将所有的printk消息输出到控制台:
   echo 8 > /proc/sys/kernel/printk

 
   如何关闭打开调试信息?
   技巧:通过ifdef定义宏来完成,需要编译debug信息在makefile中打开此宏,否则关闭。
 
   速度限制?
   为了防止大量产生log信息,导致在某些慢速设备上出现系统假死的情况,使用内核函数
int printk_ratelimit(void) 返回非0值,可以继续打印,否则跳过。
   if(printk_ratelimit())
      printk(KERN_NOTICE"The printer is still on fire\n");
 
 
3.使用proc文件系统进行调试
  
   所有proc模块需要包含#include
    
   内核函数接口:
  
  1. struct proc_dir_entry *create_proc_read_entry(const char *name,mode_t mode,
  2.                                               struct proc_dir_entry *base,
  3.                                               read_proc_t *read_proc,void *data);
  4. /*创建proc文件接口
  5. **name:要创建的文件名称
  6. **mode:该文件的保护码,0表示系统默认值
  7. **base:指定文件所在目录,base如果为NULL,表示在proc根目录下创建该文件
  8. **read_proc:该文件的read_proc函数,读取该文件时调用
  9. **data: 传递给read_proc的参数
  10. */
    1. int (*read_proc)(char *page,char **start,off_t offset,int count,int *eof,void *data);
    2. /*
    3. **page:指针指向用来写入数据的缓冲区
    4. **start:返回实际的数据写到内存页得哪个位置
    5. **offset,count:和read方法一样
    6. **eof:指向一个整形数,当没有数据可返回时,驱动程序必须设置这个参数
    7. **data:提供给驱动程序的专用数据指针,可用于内部记录
    8. */
    9.  返回值:必须返回存放到内存页缓冲区的字节数,*eof和*start也属于返回值
      
    1. void remove_proc_entry(const char *name,struct proc_dir_entry *base)
    2. /*移除proc文件
    3. **name:文件名
    4. **base:目录,和创建前面一样
    5. */
     
    4.通过监视调试
     
       有时候监视用户空间应用程序的运行情况,可以捕捉到一些小问题。
     
       strace工具,可以显示用户空间程序所发出的所有系统调用,并显示调用参数以及字符串形式的返回值。
       常用参数:
       -t    显示调用发生时间
       -T    显示调用所花费的时间
       -e    限定被跟踪的调用类型
       -o    将输出重定向到一个文件
     
    5.oops消息
      大部分错误是因为对NULL指针值取值或者因为使用了其他不正确的指针指,这些错误将导致oops消息。
     
    oops产生原因:
     
       处理器使用的几乎都是虚拟地址,这些地址通过mmu转换成物理地址,当引用一个非法指针时,分页机制无法将该地址映射到物理地址,此时处理器就会向os发出一个页面失效的信号,如果是非法地址,内核就无法换入缺失页面,如果这时处理器处于超级用户模式,系统就会产生一个oops。
     
       oops显示错误发生时处理器的状态
       EIP 指令指针
       感觉观察oops信息需要经验来支撑,平时如果遇到oops信息的话,应该不要放过自己的一个学习机会,硬着头皮看看吧~~
     
    6.调试器gdb
     
       跟踪代码调试是比较耗时的,所以不到万不得已感觉不要走这一步,呵呵~~
     
       调试内核和应用程序很不一样,用gdb对内核进行调试许多常用功能不能使用,比如设置断点观察点,单步跟踪内核函数。
     
       一个典型的gdb调试内核的命令如下:
     
       gdb /usr/src/linux/vmlinux /proc/kcore
     
       第一个是内核ELF可执行文件,不是经过压缩的zImage
       第二个是core文件的名字
     
       linux下的可装载模块是ELF格式的可执行映像,对于调试来讲,关心下面三个段:.text .bass .data
    而这三个段的信息可以在/sys/modules/scull/sections中获得,然后gdb需要做的就是:
     
       add-symbol-file ./scull.ko 0xd0832000 -s .bss 0xd0837100 -s .data 0xd0836e0
       有用的技巧命令
       print *(address) 为address传入一个十六进制的地址值,输出是该地址对应的文件以及代码行数。
       这样的话,我们可以找到某个函数指针所指的函数定义在什么地方~~~
     
    7.kdb补丁
     
       kdb是内核内置的调试器,要获得对应内核版本的补丁程序,进行patch后重新编译内核,可以在控制台按下pause或者break键进入调试状态,如果当内核发生oops或者到达摸个断点,也会启动kdb,进入下面的状态:
     
      Entering kdb(0xc0234580)on processor 0 due to keyboard Entry
      [0]kdb>
     
    当kdb运行时,内核做的每一件事都会停下,注意不要开启网络功能,除非是在调试网络驱动,一般进入单用户模式进行kdb调试内核。
    bp设置断点
    go表示继续执行
    bt查看backtrace
    mds对数据进行处理
    mm addr value 修改addr的数据为value
    阅读(1497) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~