(1) 热键: Alt + SysRq : 两个键同时按住不放,再按下面的键.
b boot. 立刻重启系统. 确认先同步和重新加载磁盘.
k 调用"安全注意键"( SAK ) 功能. SAK 杀掉在当前控制台的所有运行的进程, 给你一个干净的终端.
r 关闭键盘原始模式; 用在一个崩溃的应用程序( 例如 X 服务器 )可能将你的键盘搞成一个奇怪的状态.
s 进行一个全部磁盘的紧急同步.
u umount. 试图重新加载所有磁盘在只读模式. 这个操作, 常常在 s 之后马上调用, 可以节省大量的文件系统检查时间, 在系统处于严重麻烦时.
p 打印处理器消息.
t 打印当前任务列表.
m 打印内存信息.
(2) printk的输出:
dmesg 命令: 查看历史---曾经打印的,输出的.这是我发现的最好的方式了.
/var/log/messages : 很多printk信息,当然,最好的还是 dmesg 命令.
(3) /proc 接口
<1> 默认创建文件到 /proc/ 目录下,例如下面的伪代码 创建 /proc/my_proc :
创建: create_proc_read_entry(
"my_proc",
0744, //文件属性 777 之类的东西.
NULL, //parent dir,如果为NULL则默认到 /proc/
scull_read_procmem, //抄模板吧...没难度.
(void *)0x12345678 // client data: 自定义,传递给scull_read_procmem的最后一个参数.
);
销毁: remove_proc_entry(
"my_proc",
NULL //这个参数是 parent,NULL代表 parent= /proc/
);
<2> 可以自己创建子目录,子目录只能位于/proc/ 目录之下,当然还可以创建更深的子目录.
下面的代码创建: /proc/myporc/my_proc
static struct proc_dir_entry *parent; //要作为全局变量保存起来,销毁时候需要使用.
{
//会在 /proc/ 下创建 myproc 子目录,注意代码中调用一次这个语句,就创建一个...
parent = proc_mkdir("myproc", NULL);
create_proc_read_entry(
"my_proc",
0744,
parent,
scull_read_procmem,
(void *)0x12345678
);
}
销毁:
{
//1) 带有自定义子目录的,先删除文件
remove_proc_entry("my_proc", parent);
//2) 然后需要删除 字目录
remove_proc_entry("myproc",NULL);
}
参考代码: /home/gzw/LDD3/04/proc_test/
(4) seq_file 接口:
也是创建到/proc/目录下, 内核提供了另外一套处理函数和框架而已,
就是为了解决 proc 接口对大文件的支持不好的问题.小文件用 proc 接口很好了.
<1> 创建
struct proc_dir_entry *entry;
entry = create_proc_entry(SEQ_FILE_NAME, 0, NULL);
if (entry)
{
entry->proc_fops = &scull_proc_ops;
}
<2> 销毁
remove_proc_entry(SEQ_FILE_NAME, NULL);
<3> 原理和 scull_proc_ops
static struct file_operations scull_proc_ops = {
.owner = THIS_MODULE,
.open = scull_proc_open, //需要自己定义(其实系统也有 seq_open 函数,试了一下,读取后得到"已杀死"三个字)
.read = seq_read, //使用系统内建的: 在EXPORT TABLE中可以找到. /proc/kallsyms 中有.
.llseek = seq_lseek, //同上
.release = seq_release //同上
};
内核其实内建了seq_read,seq_lseek,seq_release,seq_open 函数.多数情况下,需要自己的open函数,其他使用内核提供的.
open函数标准模板如下:
static int scull_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &scull_seq_ops);
}
这里又来了scull_seq_ops操作,这才是主角:
static struct seq_operations scull_seq_ops = {
.start = scull_seq_start,
.next = scull_seq_next,
.stop = scull_seq_stop,
.show = scull_seq_show
};
我们必须实现上述几个函数:
scull_seq_start
scull_seq_next
scull_seq_show
当我们 cat 一个 创建的文件,会走这样的流程:
open -->start --->show -->next-->show -->next --->show 直到 next函数返回空.
<4> 原型和接口:
static int scull_seq_show(struct seq_file *s, void *v)
show()函数最重要的是 参数 v,实际上就是 start 和 next 函数的返回值.
这里用函数: seq_printf(s,"%2.2X ", *p0); 就可以让 cat 显示出来!
show()函数 返回0表示成功,其他是错误码.
static void *scull_seq_start(struct seq_file *s, loff_t *pos)
返回值,在show函数中,作为v参数传入. 相当于第一次的 next函数 .
static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
返回值,在show函数中,作为v参数传入.
static void scull_seq_stop(struct seq_file *s, void *v)
直接返回即可.
所有的逻辑完全由我们自己决定,包括那个 *pos,*v 这样的东西
我可以这样安排:一个大的二维数组,例如: static unsigned char my_seq_data_array[8][16];
用 *pos 作为 [*pos][16]
每次把对应的地址作为next的返回值.然后,show中就显示 v 指定的地址中的数据.....
参考代码: /home/gzw/LDD3/04/proc_test/
(5) insmod 之后,会产生几个虚拟文件系统的文件,下面以usb_boot_jz.ko为例:
# ls /sys/module/usb_boot_jz/
drivers/usb:skeleton -> ../../../bus/usb/drivers/skeleton
sections/ 这里存放了本驱动的全部sections,每个文件就一行,表示该section的起始地址.
sections/__mcount_loc 内容就一行内容,0xf812ded0
initstate 表示初始化状态
refcnt 引用计数,数字串
srcversion 源码版本,字符串串内容,例如: B0654CD51F7F598E4F6833F
调试器 gdb 需要 sections/ 中存放的段地址信息---因为模块不是静态的,不会出现在 静态内核中.
gdb的调试方法是把内核当做一个APP来调试:
# gdb /usr/src/linux/vmlinux /proc/kcore
vmlinux 是是非压缩的 ELF 内核可执行文件的名字,编译出来的,不是 zImage 或者 bzImage.
kcore: 用来代表内核"可执行文件", 以一个核心文件的形式, 是一个巨大的文件,
因为他代表整个的内核地址空间, 对应于所有的物理内存.当你任何时候读取这个文件时,
系统都会给你最新的内核可执行文件.
(6) gdb 调试APP是如此之简单: /home/gzw/LDD3/04/gdb_hello
<1> 编译APP必须加入 -g 选项
例如: # cc -g tst.c -o tst
<2> 使用GDB调试:
# gdb tst <---------- 启动GDB
启动后,出现gdb提示符,然后可以使用各种gdb命令
(gdb) l <-------------------- l 命令相当于list,从第一行开始例出原码。
下面就会列出源码...
help 命令可以列出帮助.
q命令退出.
可以调试内核吗?好像可以,有时间在细研究吧.
(7)其他调试内核的工具:
strace 命令: 要执行的程序用 strace 启动,可以看到调用关系,输出格式:
函数(参数1,参数2。..) =返回值
例如: close(3) = 0
// todo:下面几个有时间在看吧.
kgdb
Linux 追踪工具:
好像比较有用:
Dynamic Probes ( DProbes ):
// todo: 问题: 关于 console_loglevel
<1> 一直想寻找 uBuntu下的 console 终端,还是没找到.
<2> 单独开一个窗口: # cat /proc/kmsg 可以随时看到 内核 printk的情况,似乎不太准,有时没有.
<3> /proc/sys/kernel/printk: 记录了 printk 的级别,没有别的.好像没啥用啊.
<4> 源码: setconsole 是个APP,编译过了.但是运行时候,总是报错:
./setconsole 1
输出结果: ./setconsole: ioctl(stdin, TIOCLINUX): Invalid argument
<5> 源码: setlevel是个APP,编译成功,运行也能成功改变 /proc/sys/kernel/printk 中的值,
但是不起作用---一直试图寻找一个终端,可以随时看到printk的输出,没找到.
echo 5 > /proc/sys/kernel/printk
dmesg -n 5
./setlevel 5 #这是LDD3中提供的小程序
程序中可以使用系统调用: sys_syslog() 设置.
下面4种方法等效,并且全局作用---cat /proc/sys/kernel/printk 可以看到第一个参数变了.
重新启动后如何? 恢复默认值 4 应该是 DEFAULT_CONSOLE_LOGLEVEL, 这个值就是: 叫做console_loglevel
<6>总之: 还是没找到方法把printk直接输出到窗口,只有用 dmesg 看.
理论上是这样说的: printk中的级别与 console_loglevel 进行比较,只有当信息的日志级别小于console_loglevel这
个整数变量的值时,信息才会在当前控制台上显示出来。
验证发现: printk的级别和 console_loglevel 没啥关系! 在uBuntu下都printk都不能直接在终端显示,
只能通过 dmesg 显示.
本章调试源码:
![](http://control.cublog.cn/fileicon/rar.gif) |
文件: |
04.rar |
大小: |
473KB |
下载: |
下载 | |