访问手机版  

Linux内核|Linux内核学习培训|怎么学Linux|Linux认证,首选超级网工!

招聘|合作 登陆|注册

网络工程师培训

当前位置:网络工程师 > 技术课程 > linux > 热点关注 > linux内核

你会调试你的Linux内核吗?

时间:2018-07-05

Linux内核在使用一段时间之后,可能会出现bag,所以需要进行调试。虽然内核调试是一件艰苦的工作,但是为了运行更加流畅,还是需要进行内核调试。那怎么进行内核调试呢?
一、调试前的准备   
内核级bug具有行为不可靠,定义不清晰或者说很难再现的诸多特定,为内核级的bug跟踪和调试带来了很大的困难。   
对于一些定义不清楚地bug,问题的关键就是找到bug的源头,很多时候,当你精确地重现一个bug的时候,你就离成功不远了。
二、内核中的bug   
从隐藏在源代码中的错误到展现在目击者面前的bug,其发作往往是一系列连锁反应的事件才可能出发的。   
虽然内核调试有一定的困难,但是通过你的努力和理解,说不定你会喜欢上这样的挑战。
三、printk( )  
内核提供的格式化打印函数。  
1、printk函数的健壮性     
健壮性是printk最容易被接受的一个特质,几乎在任何地方,任何时候内核都可以调用它(中断上下文、进程上下文、持有锁时、多处理器处理时等)。    
在系统启动过程中,终端初始化之前,在某些地方是不能调用的。    
2、记录等级    
printk函数可以指定一个记录级别,内核根据这个级别来判断是否在终端上打印消息。    
记录级别定义在<linux/kernel.h>中。
调用方式:printk(KER_DEBUG “This is a debug notice!/n”);  内核用这个指定的纪录等级和当前终端的纪录等级console_loglevel比较,来决定是不是向终端打印。
3、记录缓冲区   
内核消息都被保存在一个LOG_BUF_LEN大小的环形队列中。   
关于LOG_BUF_LEN定义:   
#define __LOG_BUF_LEN     (1 << CONFIG_LOG_BUF_SHIFT)   
变量CONFIG_LOG_BUF_SHIFT在内核编译时由配置文件定义,对于i386平台,其值定义如下(在linux26/arch/i386/defconfig中):   
CONFIG_LOG_BUF_SHIFT=18     
缓冲区操作:   
①消息被读出到用户空间时,此消息就会从环形队列中删除。   
②当消息缓冲区满时,如果再有printk()调用时,新消息将覆盖队列中的老消息。   
③在读写环形队列时,同步问题很容易得到解决。   
这个纪录缓冲区之所以称为环形,是因为它的读写都是按照环形队列的方式进行操作的。
4、syslogd和klogd   
在标准的Linux系统上,用户空间的守护进程klogd从纪录缓冲区中获取内核消息,再通过syslogd守护进程把这些消息保存在系统日志文件中。klogd进程既可以从/proc/kmsg文件中,也可以通过syslog()系统调用读取这些消息。默认情况下,它选择读取/proc方式实现。klogd守护进程在消息缓冲区有新的消息之前,一直处于阻塞状态。一旦有新的内核消息,klogd被唤醒,读出内核消息并进行处理。默认情况下,处理例程就是把内核消息传给syslogd守护进程。   
syslogd守护进程一般把接收到的消息写入/var/log/messages文件中。不过,还是可以通过/etc/syslog.conf文件来进行配置,可以选择其他的输出文件。
四、OOPS
作为内核的开发者,必定将会经常处理OOPS。
1、ksymoops    
在 Linux 中,调试系统崩溃的传统方法是分析在发生崩溃时发送到系统控制台的 Oops 消息。一旦您掌握了细节,就可以将消息发送到 ksymoops 实用程序,它将试图将代码转换为指令并将堆栈值映射到内核符号。
ksymoops需要几项内容:Oops 消息输出、来自正在运行的内核的 System.map 文件,还有 /proc/ksyms、vmlinux 和 /proc/modules。
关于如何使用 ksymoops,内核源代码 /usr/src/linux/Documentation/oops-tracing.txt 中或 ksymoops 手册页上有完整的说明可以参考。Ksymoops 反汇编代码部分,指出发生错误的指令,并显示一个跟踪部分表明代码如何被调用。
#cat /proc/kallsyms     
c0100240   T       _stext     
c0100240   t       run_init_process     
c0100240   T      stext     
c0100269   t       init
五、内核调试配置选项  
在编译内核的时候,为了方便调试和测试代码,内核提供了许多配置选项。
调试原子操作  
从内核2.5开发,为了检查各类由原子操作引发的问题,内核提供了极佳的工具。  
内核提供了一个原子操作计数器,它可以配置成,一旦在原子操作过程中,进城进入睡眠或者做了一些可能引起睡眠的操作,就打印警告信息并提供追踪线索。  
所以,包括在使用锁的时候调用schedule(),正使用锁的时候以阻塞方式请求分配内存等,各种潜在的bug都能够被探测到。  
下面这些选项可以最大限度地利用该特性:  
CONFIG_PREEMPT = y  
CONFIG_DEBUG_KERNEL = y  
CONFIG_KLLSYMS = y  
CONFIG_SPINLOCK_SLEEP = y
六、引发bug并打印信息
1、一些内核调用可以用来方便标记bug,提供断言并输出信息。最常用的两个是BUG()和BUG_ON()。
当调用这两个宏的时候,它们会引发OOPS,导致栈的回溯和错误消息的打印。
2、dump_stack()    
有些时候,只需要在终端上打印一下栈的回溯信息来帮助你调试。这时可以使用dump_stack()。这个函数只在终端上打印寄存器上下文和函数的跟踪线索。    
if (!debug_check) {        
printk(KERN_DEBUG “provide some information…/n”);        
dump_stack();    
}