一、内核开发的配置选项
1.CONFIG_DEBUG_KERNEL
该选项仅仅使得其他的调试选项可以。我们应该打开该选项,但它本身不会打开所以的调试功能。
2.CONFIG_DEBUG_SLAB
这是一个非常重要的选项,他打开内核内存分配函数中的多个类型检查:打开该检查后,就可以检测许多内存溢出及忘记初始化的错误。在将已分配内存返回给调用者之前,内核将把其中的每个字节设置为0xa5,而在释放后将其设置为0x6b。可在自己去的程序的输出或用过在oops信息中看字符判断问题所在。在打开调试选项后,内核还会在每个已分配内存对象的前面或后面防止特殊防护值。这样,当防护值发生改变是,内核就可以知道有些代码超出了内存的正常访问范围,并警报。
3.CONFIG_DEBUG_PAGELLOC
在释放时,全部内存页从内核地址空间移出。该选项大大降低了运行速度,但可以快速定位特定的内存损坏错误的所在位置。
4.CONFIG_DEBUG_SPINLOCK_SLEEP
该选项将检查用于自旋锁时的休眠企图。若调用可能引起休眠的函数,这个选项也会生效,即使该函数可能不会导致真正的休眠。
5.CONFIG_INIT_DEBUG
标记为_ init(或 _initdata)的符号将会在系统初始化或模块装载后被丢弃。该选项用来检查初始化后对于初始化的内存空间的访问企图。
6.CONFIG_DEBUG_INFO
该选项使内核的构造包含完整到的调试信息。若用gdb调试内核,则需要这些消息,当使用gdb时,还需要打开CONFIG_FRAME_POINTER选项。
7.CONFIG_DEBUG_STACKOVERFLOW || CONFIG_DEBUG_STACK_USAGE
这些选项可帮助跟踪内核栈的溢出问题。栈溢出的确切信号是不包含任何合理的反向跟踪信息的oops清单。第一个选项将在内核中增加明确的溢出检查。第二个选项将让内核监视栈的使用,并通过SysRq按键输出一些统计信息。
8.CONFIG_KALLSYMS
该选项出现在General setup/Standard features菜单中,将在内核中包含符号信息。该选项默认是打开的,该符号信息用于调试上下文。
9.CONFIG_IKCONFIG || COFIG_IKCONFIG_PROC
这些选项出现在General setup菜单中,会让完整的内核配置状态包含到内核中,可通过/proc访问。
10.CONFIG_ACPI_DEBUG
该选项出现在Power management/ACPI菜单中。该选项在打开ACPI(Advanced Configuration and Power Interface,高级配置和电源接口)中的详细调试信息。
11.CONFIG_DEBUG_DRIVER
在Device drivers菜单中,该选项打开驱动程序核心中的调试信息,帮助追踪底层支持代码中的问题。
12.CONFIG_SCSI_CONSTANTS
该选项出现在Device drivers/SCSI device support菜单中,它将打开详细的SCSI错误信息。
13.CONFIG_INPUT_EVBUG
该选项在Device drivers/Input device support中找到,它会打开对输入事件的详细记录。若要求针对输入设备编写驱动程序,则可以使用该选项。该选项会记录键入的任何东西,会导致安全问题。
14.CONFIG_PROFILING
该现象可在Profiling support(剖析支持)菜单中找到,剖析通常用于系统性能的调节,但对跟踪内核挂起及相关问题也会有所帮助。
二、通过打印调试
调试内核代码是,利用printk来完成调试。
printk可以通过附加不同的日志级别,或者消息优先级来根据这些级别锁表示的严重程序对消息进行分类。
在编译时将表示日志级别的宏和消息文本拼接在一起,无逗号。
例如:
调试信息:
printk(KERN_DEBUG "Here I am:%s:%i\n",_ _FILE _ _, _ _LINE _ _);
临界信息:
printk(KERN_CRIT "I'm trashed; giving up on %p\n",ptr);
八种可用的日志级别字符串:
1.KERN_EMERC
用于紧急事件消息,一般是系统崩溃前的提示信息。
2.KERN_ALERT
用于需要立即采取动作的情况。
3.KERN_CRIT
临界状态,通常涉及严重的硬件或软件操作失败。
4.KERN_ERR
用于报告错误状态。设备驱动程序会经常使用KERN_ERR来报告来自硬件的问题。
5.KERN_WARNING
对可能出现的情况进行警告,但着了情况通常不会对系统造成严重问题。
6.KERN_NOTICE
有必要进行提示的正常情况。许多与安全相关的状况用这个级别进行汇报。
7.KERN_INFO
提示性信息,很多驱动程序在启动的时候以这个级别来打印出它们找到的硬件信息。
8.KERN_DEBUG
用于调试信息
三、重定向控制台消息
内核可以将消息发送到一个指定的虚拟控制台。控制台就是当前的虚拟终端,可以在任何一个控制台设备上调用ioctl(TIOCLINUX)来指定接收消息的其他虚拟终端。
可选择专门用来接收内核消息的控制台
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
char bytes[2] = {11,0}; /* 11 is the TIOCLINUX cmd number */
if (argc==2) bytes[1] = atoi(argv[1]); /* the chosen console */
else {
fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1);
}
if (ioctl(STDIN_FILENO, TIOCLINUX, bytes)<0) { /* use stdin */
fprintf(stderr,"%s: ioctl(stdin, TIOCLINUX): %s\n",
argv[0], strerror(errno));
exit(1);
}
exit(0);
}
四、消息如何被记录
①printk函数将消息写到一个长度为_ LOG_BUF_LEN字节的循环缓冲区中(在配置内核时为 _LOG_BUF_LEN指定4KB-1KB之间的值)
②唤醒任何正在等待消息的进程(即睡眠在syslog系统调用上的进程),或正在读取/proc/kmsg的进程,dmesg可以不刷新缓冲区。
③停止klogd后读取/proc/kmsg文件会阻塞进程,如果已有klogd或其他进程读取时,不能直接读取该文件,避免竞争。
④缓冲区写满会覆盖,可能丢失旧数据,
⑤klogd读取内核消息发送到syslogd。
⑥syslogd根据/etc/syslog.conf找出处理这些数据的方法。
⑦syslogd根据功能和优先级对消息进行区分。
⑧内核消息由LOG_KERN工具记录,并且与printk中对应的优先级记录,如果没有运行klogd,数据将保留在缓冲区。
⑨klogd-(file)可以只是klogd将消息保存到某个特定文件。
五、开启及关闭消息
全局的开启或禁止调试信息,并对个别消息进行开关控制。
定义一个宏,在需要时,这个宏展开为一个printk调用
是否定义PDEBUG取决于是否定义SCULL_DEBUG,它能根据代码锁运行额环境来选择合适的方式显示信息。
在内核态时:使用内核调用printk
在用户空间:使用libc调用fprintf,并输出到标准错误设备。
符号PDEBUGG什么也不做,它可将打印语句注释掉,而不必把他们完全删除。
六、速度限制
问题的产生:利用printk产生上千条消息,从而让日志信息充满控制台,更可能使系统日志文件溢出。若使用慢速控制台设备,过高的消息输出速度会导致系统变慢,甚至时系统无法正常相应。
设置一个标志位来发出警告,并且在该标志位被设置时不在打印任何消息。(避免同一个数据多次发送)
int printk_ratelimit(void);
//通过跟踪发送到控制到的消息数量工作。
若输出速度超过一个阈值,则返回零。
例子:
//
在打印一条可能被重复的消息之前,应调用上面函数。若该函数返回一个非零值,
则可以继续并打印消息,否则跳过。
if(printk_ratelimit()){
printk(KERN_NOTICE "error\n");
}
七、打印设备号
//这两个宏均是将设备编号打印到特定的缓冲区。
int print_dev_t(char *buffer, dev_t dev);
返回的是打印的字符数
char *format_dev_t(char *buffer, dev_t dev);
返回的是缓冲区,可直接作为调用printk时的参数使用。
八、通过查询调试
①大量使用printk会显著降低系统性能syslogd试图把每件事都记录到磁盘上
②在/etc/syslogd.conf中日志文件名字前加一个减号可以避免实时刷新磁盘
③获取信息最好的方法是需要的时候才去查询