Linux内核调试技术
来源:互联网 发布:上海plc编程培训 编辑:程序博客网 时间:2024/05/25 05:38
在前面已经建议过:学习编写驱动程序要构建安装自己的内核(标准主线内核)。最重要的原因之一是:内核开发者已经建立了多项用于调试的功能。但是由于这些功能会造成额外的输出,并导致性能下降,因此发行版厂商通常会禁止发行版内核中的调试功能。
为了实现内核调试,我在内核配置上增加了几项:
Kernel hacking --->
[*] Magic SysRq key
[*] Kernel debugging
[*] Debug slab memory allocations
[*] Spinlock and rw-lock debugging: basic checks
[*] Spinlock debugging: sleep-inside-spinlock checking
[*] Compile the kernel with debug info
[*] Magic SysRq key
Device Drivers --->
Generic Driver Options --->
[*] Driver Core verbose debug messages
General setup --->
[*] Configure standard kernel features (for small systems) --->
[*] Load all symbols for debugging/ksymoops
等等。
二、通过打印调试
首先,printk有8个loglevel,定义在<linux/kernel.h>中:
#define KERN_EMERG "<0>" //用于紧急事件消息,它们一般是系统崩溃之前提示的消息
#define KERN_ALERT "<1>" //
用于需要立即采取动作的情况
#define KERN_CRIT "<2>" //临界状态,通常涉及严重的硬件或软件操作失败
#define KERN_ERR "<3>" //用于报告错误状态,设备驱动程序会经常使用KERN_ERR来报告来自硬件的问题
#define KERN_WARNING "<4>"
//对可能出现问题的情况进行警告,但这类情况通常不会对系统造成严重问题
#define KERN_NOTICE "<5>" //有必要进行提示的正常情形。许多与安全有关的情况用这个级别进行汇报
#define KERN_INFO "<6>" //提示性信息。很多驱动程序在启动的时候以这个级别来打印它们找到的硬件信息
#define KERN_DEBUG "<7>"
//用于调试信息
所以printk()可以这样用:printk(KERN_INFO "Hello, world!\n");
未指定日志级别的printk("Hello, world!\n")采用的默认级别是DEFAULT_MESSAGE_LOGLEVEL,这个宏在kernel/printk.c中被定义为整数4,即对应KERN_WARNING。
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
在/proc/sys/kernel/printk 会显示4个数值(可由echo修改),如下图。
#cat /proc/sys/kernel/printk
#echo 8 > /proc/sys/kernel/printk //通过echo修改当前日志级别为8
根据日志级别,内核可能会把消息打印到当前控制台上,这个控制台可以是一个字符模式的终端、一个串口打印机或一个并口打印机。当优先级小于console_loglevel这个整数变量的值,消息才能显示出来,而且每次输出一行(如果不以newline字符结尾,则不会输出)。
如果系统同时运行了klogd和syslogd,则无论console_logleve为何值,内核消息都会追加到/var/log/messages(用户空间)中。
如果klogd没有运行,这些消息就不会传递到用户空间,这时只能查看/proc/kmsg文件(使用dmesg命令可以轻松做到)。
变量console_loglevel的初始值DEFAULT_CONSOLE_LOGLEVEL也定义在/kernel/printk.c中:
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
2.1 而在运行是改变console_loglevel的程序《Linux设备驱动程序(第3版)》提供,如下setlevel.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define __LIBRARY__ /* _syscall3 and friends are only available through this */
#include <linux/unistd.h>
/* define the system call, to override the library function */
_syscall3( int syslog, int type, char *bufp, int len);
int main(int argc, char **argv)
{
int level;
if(argc==2)
{
level = atoi(argv[1]); /* the chosen console */
}else
{
fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1);
}
if( syslog(8,NULL,level) < 0 )
{
fprintf(stderr,"%s: syslog(setlevel): %s\n", argv[0],strerror(errno));
exit(1);
}
exit(0);
}
#./setlevel 2 //设置日志级别,我测试没有设置成功???
2.2 重定向到控制台的程序《Linux设备驱动程序(第3版)》提供,如下setconsole.c:
#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);
}
#./setconsole tty0 //参数总是错误的,不知道如何设置???
三、开启及关闭消息
下面给出了一个调用printk的编码方法,它可个别或全局地开关printk语句。是否定义符号PDEBUG取决于是否定义了SCULL_DEBUG,并且,它能根据代码所运行的环境来选择合适的方式显示信息。
这些宏被证实是非常有用的,仅有的缺点是每次开启和关闭调试都要重新编译模块。
另一种方法:使用C条件语句,它在运行时执行,因此可以在程序运行期间打开或者关闭,但每次代码执行时系统都要进行额外的处理,甚至在禁用消息后仍然会影响性能,而有时这种性能损失是无法接收的。
#undef PDEBUG
//取消对PDEBUG的定义,以防重复定义
//表明打开调试并处于内核空间
#ifdef SCULL_DEBUG
# ifdef __KERNEL__
//表明处于用户空间
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)
# else
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) //调试被关闭:不作任何事情
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) //不作任何事情
,仅仅是占个符号位
Makefile中要添加的语句:
DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG
else
DEBFLAGS = -O2
endif
CFLAGS += $(DEBFLAGS)
速度限制
为了避免printk重复输出过快而阻塞系统,内核使用以下函数跳过部分输出:
int printk_ratelimit(void);
典型的应用如下:
if (printk_ratelimit( ))
printk(KERN_NOTICE "The printer is still on fire\n");
可以通过修改/proc/sys/kernel/printk_ratelimit(重开信息前应等待的秒数)和/proc/sys/kernel/printk_ratelimit_burst(在速度限制前可接受的信息数)来定制printk_ratelimit的行为。
打印设备编号
Linux还提供了打印设备编号的宏(在<linux/kdev_t.h>中定义):
int print_dev_t(char *buffer, dev_t dev);
char *format_dev_t(char *buffer, dev_t dev);
两个函数的唯一区别是:print_dev_t返回打印字符数,format_dev_t返回缓冲区指针。注意缓冲区char *buffer的大小应至少有20B。
三、通过查询调试
驱动开发人员可以用如下方法对系统进行查询:在/proc文件系统中创建文件、使用驱动程序的ioctl方法、以及通过sysfs导出属性等。
使用/proc文件系统
/proc文件系统是一种特殊的、由软件创建的文件系统,内核使用他向外界导出信息。/proc下面的每个文件都绑定于一个内核函数,用户读取其中的文件时,该函数动态的生成文件的“内容”。
如以前用过的:/proc/modules列出的是当前载入模块的列表。
- Linux 内核调试技术
- Linux内核调试技术
- linux 内核调试技术
- Linux内核调试技术
- linux内核的调试技术
- linux 内核oops调试技术
- Linux内核调试技术指南
- Linux内核调试技术指南
- Linux内核Kprobes调试技术
- linux 内核oops调试技术
- linux内核调试平台与内核调试技术
- linux内核调试技术之printk
- 嵌入式Linux完全开发手册-------------Linux内核调试技术
- 内核调试技术
- 内核调试技术 profs
- 内核调试技术
- 内核调试技术
- kprobes内核调试技术
- UVa 10034 Freckles 解答
- STL之insert_iterator
- WPF登录窗体
- 数据库建表的十四个技巧
- JavaCookbook-3.字符串与4.正则表达式
- Linux内核调试技术
- 程序员学习之路
- 获取sqlserver数据库中所有库、表、字段名的方法
- Linux之Vim使用
- 协调多个对象之间的交互——中介者模式(一)
- JAVA中int、String的类型转换
- 网站特殊字体处理
- Android培训---支持不同的平台版本
- IOS开发之——改进iOS客户端的升级提醒功能