ldd3学习笔记:调试技术
来源:互联网 发布:淘宝自动秒杀软件 编辑:程序博客网 时间:2024/05/17 04:48
1.内核中支持的调试选项打开:kernel hacking菜单
CONFIG_DEBUG_KERNEL:使其他调试选项可用; 但不激活任何特性.
CONFIG_DEBUG_DRIVER:在"Device drivers"下 打开驱动核心的调试信息,
2.prink打印调试:
1). printk函数中能够指定优先级,在头文件 <linux/kernel.h> 里定义了0-7共8个级别的输出优先级,数字越小,优先级越高;未指定优先级的prink采用默认级别DEFAULT_MESSAGE_LOGLEVEL, 这个宏在kernel/printk.c中定义为一个整数。
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
头文档linux/printk.h中宏定义了8个级别,0-8从高到低分别是:
KERN_EMERG, KERNEL_ALERT, KERN_CRIT, KERN_ERR, KERN_WARNING, KERN_NOTICE, KERN_INFO, KERN_DEBUG
2). 根据日志级别,内核可能会将消息打印到当前控制台上。当优先级小于console_loglevel时,消息才能显示出来。控制台输出级别 console_loglevel 初始化成 DEFAULT_CONSOLE_LOGLEVEL。
a.能够使用系统调用sys_syslog或klogd -c来修改console_loglevel值。注意要改变当前值, 你必须先杀掉 klogd, 接着使用 -c 选项重启它.
b.也能够直接通过文档 /proc/sys/kernel/printk 读写控制台记录级别(这个文档包含4个整数值,前两个表示系统当前的优先级和缺省优先级), 指定一个整数在 1 和 8 之间, 包含 1 和 8. 如果它设为 1, 只有 0 级消息( KERN_EMERG )到达控制台; 如果它设为 8, 所有消息, 包括调试消息, 都显示.
例如, 你可以使所有内核消息出现在控制台, 通过简单地输入:
#echo 7 > /proc/sys/kernel/printk
#cat /proc/sys/kernel/printk
7 4 1 7
c.也能够指定显示在其他控制台,通过调用ioctl(TIOCLINUX)或shell命令setconsole来配置。在 setconsole 里, 使用子命令 11, 下一个字节(存于 bytes[1])指定虚拟控制台.4个整数值分别是:当前的loglevel、默认loglevel、最小允许的loglevel、引导时的默认loglevel。
使用特殊的 ioctl 命令 TIOCLINUX来设定控制台设备号,(TIOCLINUX在drivers/char/tty_io.c 里定义):
char bytes[2] = {11,3}; /* 11 is the TIOCLINUX cmd number , 3 is the the chosen console number */
ioctl(STDIN_FILENO, TIOCLINUX, bytes); /* use stdin */
如果klogd没有运行,这些消息就不会传递到用户空间,此时只能查看/proc/kmsg文件(使用dmesg命令轻松做到)。
3)消息如何被记录:
printk函数将消息写到一个长度为__LOG_BUF_LEN字节的环形缓冲区,可在配置内核时为__LOG_BUF_LEN指定为4KB~1MB之间。
如果循环缓冲区填满了,printk就绕回缓冲区的开始出填写新的数据,将覆盖最早的数据。
对/proc/kmsg进行读操作,日志缓冲区被读取的数据就不再保留。
syslog系统调用能通过选项返回日志数据并保留数据。
dmesg命令可在不刷新缓冲区数据的情况下获得缓冲区内容。这个命令将缓存区的整个内容返回给 stdout, 不管它是否已经被读过.
klogd 进程运行, 它获取内核消息并分发给 syslogd, syslogd 接着检查 /etc/syslog.conf 来找出如何处理它们.给 klogd 指定一个 -f (文件) 选项来指示它保存消息到一个特定的文件。
(todo:klogd, syslogd和dmesg命令详解)
4) 可通过预处理指令开启和关闭调试信息
5)为了避免printk重复输出过快而阻塞系统,内核使用以下函数跳过部分输出:
int printk_ratelimit(void); 当输出级别超过一个限度, printk_ratelimit 开始返回 0 并使消息被扔掉.
应用例子:
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的行为。
3.查询调试
查询系统的三种方法:在 /proc 文件系统下创建文件; 使用 ioctl 驱动方法;借助 sysfs 输出属性(驱动模型,14章介绍)
方法一:在 /proc 文件系统下创建文件
1) /proc 文件系统是动态的, 因此你的模块可以在任何时候添加或去除条目.大部分时间, /proc 条目是只读的文件.
2)所有使用 /proc 的模块应当包含 <linux/proc_fs.h> 来定义正确的函数.
int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);
连接它到 /proc 层次中的一个入口项. 使用一个 creat_proc_read_entry 调用,可以在目录base(NULL 时默认为在 /proc 根下创建 )下创建一个名为name的文件。
struct proc_dir_entry *create_proc_read_entry(const char *name,mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data);
a
模块卸载后去除/proc 中的入口:
remove_proc_entry("scullmem", NULL /* parent dir */);
注意:内核不会检查名字是否已经注册, 如果用同样的名字注册两个入口,在存取或移出入口时都是不能区分的。
4)采用seq_file 接口来创建大内核虚拟文件.
包含 <linux/seq_file.h>.
接着你必须创建 4 个 iterator 方法, 称为 start, next, stop, 和 show.
void *start(struct seq_file *sfile, loff_t *pos);
void *next(struct seq_file *sfile, void *v, loff_t *pos); //next 函数应当移动 iterator 到下一个位置
void stop(struct seq_file *sfile, void *v); //当内核处理完 iterator, 它调用 stop 来清理
int show(struct seq_file *sfile, void *v); //内核调用 show 方法来真正输出有用的东西给用户空间,这个方法应当创建序列中由 iterator v 指示的项的输出. 不应当使用 printk
现在已有了一个完整的 iterator 操作的集合, scull 必须包装起它们, 并且连接它们到 /proc 中的一个文件. 第一步是填充一个 seq_operations 结构:
static struct seq_operations scull_seq_ops = { .start = scull_seq_start, .next = scull_seq_next, .stop = scull_seq_stop, .show = scull_seq_show};
创建一个 file_operations 结构(是的, 和字符驱动使用的同样结构) 来实现所有内核需要的操作, 来处理文件上的读和移动. 幸运的是, 这个任务是简单的. 第一步是创建一个 open 方法连接文件到 seq_file 操作:
static int scull_proc_open(struct inode *inode, struct file *file){ return seq_open(file, &scull_seq_ops);}
调用 seq_open 连接文件结构和我们上面定义的序列操作. 事实证明, open 是我们必须自己实现的唯一文件操作, 因此我们现在可以建立我们的 file_operations 结构:
static struct file_operations scull_proc_ops = { .owner = THIS_MODULE, .open = scull_proc_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release };
最后的步骤是调用低层的 create_proc_entry创建 /proc 中的实际文件:
struct proc_dir_entry *create_proc_entry(const char *name,mode_t mode,struct proc_dir_entry *parent);
entry = create_proc_entry("scullseq", 0, NULL);if (entry) entry->proc_fops = &scull_proc_ops;
ioctl是一个系统调用, 作用于一个文件描述符; 作为一个使用 /proc 文件系统的替代, 你可以实现几个用来调试用的 ioctl 命令.
这种方式使用 ioctl 来获取信息有些比使用 /proc 困难, 因为你需要另一个程序来发出 ioctl 并且显示结果. 必须编写这个程序, 编译, 并且与你在测试的模块保持同步. 另一方面, 驱动侧代码可能容易过需要实现一个 /proc 文件的代码.
有时候 ioctl 是获取信息最好的方法, 因为它运行比读取 /proc 快. 如果在数据写到屏幕之前必须做一些事情, 获取二进制形式的数据比读取一个文本文件要更有效. 另外, ioctl 不要求划分数据为小于一页的片段.
ioctl 方法的另一个有趣的优点是信息获取命令可留在驱动中, 当调试被禁止时, 不象对任何查看目录的人都可见的 /proc 文件, 不记入文档的 ioctl 命令可能保持不为人知. 另外, 如果驱动发生了怪异的事情, 它们仍将在那里. 唯一的缺点是模块可能会稍微大些.
Unable to handle kernel NULL pointer dereference at virtual address 00000000
EIP is at faulty_write+0x4/0x10 [faulty]
Unable to handle kernel paging request at virtual address ffffffff
EIP is at 0xffffffff
EIP返回0xffffffff. 同时,我们只看到部分的调用堆栈( vfs_read 和 faulty_read 丢失 ), 内核抱怨一个"坏 EIP 值". 这个抱怨和在开头列出的犯错的地址 ( ffffffff ) 都暗示内核堆栈已被破坏.
Unable to handle kernel paging request at virtual address 0xa5a5a5a5a5
EIP is at 0xa5a5a5a5a5
gdb, kdb, kgdb,UML(用户模式linux), LTT(linux追踪工具), DProbes(动态探针)
- ldd3学习笔记:调试技术
- LDD3学习笔记(7):调试技术
- LDD3读书笔记----调试技术
- LDD3学习笔记(一)
- LDD3源码分析之调试技术
- LDD3源码分析之调试技术
- LDD3源码分析之调试技术
- LDD3源码分析之调试技术
- <Debugging Techniques> LDD3 学习笔记
- linux调试技术学习笔记
- LDD3源码学习笔记之scull_pipe转
- LDD3学习笔记《三》第四章
- LDD3学习笔记(11):内存分配
- LDD3学习笔记(13):中断处理
- LDD3学习笔记(15):PCI驱动
- LDD3学习笔记(16):USB驱动
- LDD3学习笔记(19):块驱动
- LDD3学习笔记(20):网络驱动
- IPhone 开发经验教训总结 -- 仅供参考
- SDCard存储和File存储 的总结
- Struts2 Preparable与ModelDriven
- 虚拟机装苹果系统全过程
- shell下取得字符串的md5值
- ldd3学习笔记:调试技术
- iOS Obj-C
- 精彩文摘
- 看别人的代码不能想当然
- UML图中的符号说明
- 表cache/nocache
- 14.2 以并行方式运行的图形效果
- 创建android下repo的mirror镜像然后内网访问
- 李彦宏谈创业:不被关注是种幸福