您的位置:首页 > 运维架构 > Linux

LINUX内核设计与实现之调试

2013-11-09 15:00 176 查看
内核调度主要靠经验和对整个操作系统的把握.

18.1 调度前需要准备什么

重现BUG.

18.2 内核中的BUG

引发内核中的BUG的原因如下:

.同步;

.定时限制;

.竞争条件.

18.3 printk()

在终端没有初始化之前,printk()函数是不可用的.比如在setup_arch()函数之前.不过可以用printk()函数的变体--early_printk().不过此函数和平台息息相关.

Printk()函数的等级记录.如:

Printk(KERN_DEBUG "This is a debug notice!\n");

内核用这个指定的记录等级和当前终端等级console_loglevel来决定是不是向终端上打印.如果比当前终端等级console_loglevel数值小,则打印输出.默认等级为DEFAULT_MESSAGE_LOGLEVEL.各等级如下表所示:



自上而下依次值为<0>到<7>.

等级记录(loglevel)定义在文件<linux/kernel.h>.

内核消息都被保存在一个LOG_BUF_LEN大小的环形队列中.该缓冲区大小可以编译时通过CONFIG_LOG_BUF_SHIFT进行调整.单处理器上默认是16KB.

内核消息的实现方式:

标准的LINUX系统中,用户空间的守护进程klogd从记录缓冲区中获取内核消息.再通过syslogd守护进程将它们保存在系统日志文件中.klogd程序即可以从/proc/kmsg文件中也可以通过syslog()系统调用读取这些消息.

Syslogd守护进程负责把它接收到的所有消息加进一个文件中,该文件默认是/var/log/messages,可以通过/etc/syslog.conf配置文件重新指定;

Klogd守护进程负责读取内核消息.

18.4 oops

内核异常表现谓之oops.下面以一个实例进行解说一个oops的意义:



红色框是出现oops寄存器的值.一般来说,我们可以通过查看哪个寄存器包含了NULL(一个所有位都为零的数值)进而找出是函数的哪个变量的值不正常.

蓝色框是出现oops函数回溯栈.往往这些信息我们更需要.如蓝色框部分提供给我们的信息如下:

机器处于空闲状态,正在进行执行idle循环,由cpu_idle()反复调用default_idle().此时定时器产生中断了,它引起了对定时器的处理.tulip_timer()这个定时器处理函数被调用,而就是它引用了空指针.我们甚至通过偏移量(像0x128/0x1c4这些出现在函数左侧的数字)找出导致问题的语句.

18.5 内核调试配置选项

在编译的时候,为了方便调试和测试代码,内核提供了许多配置选项.这些选项都在内核配置编辑器的内核开发(Kernel Hacking)菜单项中,它们都依赖于CONFIG_DEBUG_KERNEL.主要调度选项如下:

Slab layer debugging(slab层调试选项);

High-memory debugging(高端内存调试选项);

I/O mamping debugging(I/O映射调试选项);

Spin-lock debugging(自旋锁调试选项);

Stack-overflow checking(栈溢出检查选项);

Sleep-inside-spinlock checking(自旋锁内睡眠选项);

调度原子操作

如果原子操作代码里面调用了可能引起睡眠的代码的话,后果将不堪设想.因此,内核提供了一个原子操作计数器.它可以被配置成一旦在原子操作过程中进程进入睡眠或做了一些可能引起睡眠的操作,就打印警告信息并提供追踪线索.下面的这些选项可以最大限度地利用该特性:

CONFIG_PREEMPT=y

CONFIG_DEBUG_KERNEL=y

CONFIG_KALLSYMS=y

CONFIG_SPINLOCK_SLEEP=y

18.6 引发BUG并打印信息

一些内核调用可以用来方便标记bug,提供断言并输出信息.最常用的两个是BUG()和BUG_ON().当被调用的时候,它们会引发oops,导致栈回溯和错误信息的打印.

Panic()的调用可以引发更严重的错误.此函数不但会打印错误消息,还会挂起整个系统.

18.8 内核调度器

常用的内核调度器主要有gdb、kgdb和kdb.

18.9 内核调度的常用手段

1).用UID作为选择条件

一般情况下,我们在重新封装了内核一些功能函数的时候,要保留原来的功能函数.满足一定的条件就去执行我们编写的功能函数.如下:



2).使用条件变量

实现思想和上述类似,用条件变量选择其中一分支执行我们重新封装的函数.

3).使用统计变量

主要统计内核里面某些事件的规律.

4).限制重复频率

如果我们不加限制在内核中添加printk()函数,或者某个被频繁调用的函数里面代入了printk()函数.屏幕往往布满打印信息.因此,我们需要作一定的限制.示意代码如下:

Sttic unsigned long prev_jiffy = jiffies;

If(time_after(jiffies,prev_jiffy + 2 * HZ))

{

Prev_jiffy = jiffies;

Printk(KERN_ERR "blah blah blah\n");

}

18.10 内核BUG

在实际开发中,往往某个版本存在某种BUG现象,而旧版本不会,可通过比较相关代码找出问题症结所在.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: