驱动程序开发的重点就在于驱动、系统的调试了,因此,掌握linux驱动的调试方法,是linux系统工程师必须掌握的技能。这里介绍驱动开发中常用的几种调试手段:
l
利用printk
l
查看OOP
消息
l
利用strace
l
利用内核内置的hacking
选项
l
利用ioctl
方法
l
利用/proc
文件系统
l
使用kgdb
1.1.1 利用printk
这是驱动开发中最朴实无华,同时也是最常用和有效的手段。printk函数会将内核信息输出到内核信息缓冲区中。为了避免缓冲区数据无限扩大,它使用了环形缓冲区的机制。当然,如果塞入的信息过多,之前的信息将会被冲刷掉。
在系统启动的过程中,我们可以使用串口终端监控打印信息,这些打印信息有助于我们分析系统的运行情况。进入系统之后,我们仍然可以使用dmesg命令查看。有时候我们需要打印更多的信息,有时候我们需要部分信息就可以了,在某种情况下,我们甚至不需要任何打印信息,但是设备要能正常工作。打印信息能够帮助分析系统运行情况的同时,它也能帮助“外人”查看设备信息,造成数据外泄。比如POS机,真正通过过银行标准认证的POS机是不允许有内核打印信息的。这时候,printk函数就大显身手了。
printk
的功能与我们经常在应用程序中使用的printf
是一样的,不同之处在于printk
可以在打印字符串前面加上内核定义的宏,这个宏定义了printk
的消息级别。printk
函数定义了8
个级别,依次对应级别0~7
,其数值越大,表示级别越低,表明对应的消息越不重要。第0
级为紧急事件级,第7
级为调试级,如下清单为printk
的级别定义。
- #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>" /* 用于调试信息 */
复制代码
/proc/sys/kernel/printk文件表述了printk的输出等级,我们可以查看当前输出等级,如下图所示:
可见,该文件有四个数值,它们对应的属性如下:
l
控制台日志级别:优先级高于该值的消息将被打印至控制台。
l
默认的消息日志级别:将用该优先级来打印没有优先级的消息。
l
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)。
l
默认的控制台日志级别:控制台日志级别的默认值。
也就是说,第一个7
表示级别高于(小于)7
的消息才会被输出到控制台,第二个4
表示如果调用printk
时没有指定消息级别(宏)则消息的级别为4
,第三个1
表示接受的最高(最小)级别是1
,第四个7
表示系统启动时第一个7
原来的初值是7
。
通过如下命令可以使Linux内核的任何printk都被输出:
- echo 8 > /proc/sys/kernel/printk
复制代码
我们在复杂驱动的开发过程中,为了调试会在源码中加入成百上千的printk语句。而当调试完毕形成最终产品的时候必然会将这些printk语句删除,这势必会增加我们的工作量。可以通过封装更高级的宏来减轻我们的工作量。
- #define LEDS_DEBUG
- #undef PDEBUG /* undef it, just in case */
- #ifdef LEDS_DEBUG
- #ifdef __KERNEL__
- /* This one if debugging is on, and kernel space */
- #define PDEBUG(fmt, args…) printk( KERN_EMERG "leds: " fmt, ## args)
- #else
- /* This one for user space */
- #define PDEBUG(fmt, args…) fprintf(stderr, fmt, ## args)
- #endif
- #else
- #define PDEBUG(fmt, args…) /* not debugging: nothing */
- #endif
- #undef PDEBUGG
- #define PDEBUGG(fmt, args…) /* nothing: it’s a placeholder */
复制代码
这时如果想打印调试消息,可以用PDEBUG,如果不想看到该调试消息,只需要简单的将PDEBUG改为PDEBUGG即可。而当我们调试完毕形成最终产品时,只需要简单地将第1行注释掉即可。
上边那一段代码中的__KERNEL__是内核中定义的宏,当我们编译内核(包括模块)时,它会被定义。如果不明白代码中的…和##,请查阅gcc关于预处理部分的相关资料。
1.1.1 查看Oops消息
Oops是指当一段正常运行的内核程序执行一段不可预知的程序时,所产生的程序偏差。它会以错误的打印提示告知用户。比如当内核访问一个并不存在的虚拟地址,程序跑偏等。Oops信息会被打印到控制台,并写入系统打印的环形缓冲区。在移植驱动的过程中,经常会遇到内核打印出一长串莫名其妙的信息,我们可以分析这些信息来还原程序执行现场。
我们编写一个简单的字符设备驱动,让他产生Oops,让读写函数均访问0地址,程序如下:
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/delay.h>
- #include <linux/platform_device.h>
- #include <mach/hardware.h>
- #include <asm/mach-types.h>
-
- static ssize_t x4412_Oops_read(struct device *dev, struct device_attribute *attr, char *buf)
- {
- int *p = 0;
- *p = 1;
-
- return 0;
-
- static ssize_t x4412_Oops_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
- {
- int *p = 0;
- *p = 1;
-
- return 0;
- }
-
- static DEVICE_ATTR(Oops, 0666, x4412_Oops_read, x4412_Oops_write);
-
- static struct attribute * x4412_Oops_sysfs_entries[] = {
- &dev_attr_Oops.attr,
- NULL,
- };
-
- static struct attribute_group x4412_Oops_attr_group = {
- .name = NULL,
- .attrs = x4412_Oops_sysfs_entries,
- };
-
- static int x4412_Oops_probe(struct platform_device *pdev)
- {
- return sysfs_create_group(&pdev->dev.kobj, &x4412_Oops_attr_group);
- }
-
- static int x4412_Oops_remove(struct platform_device *pdev)
- {
- sysfs_remove_group(&pdev->dev.kobj, &x4412_Oops_attr_group);
- return 0;
- }
-
- #define x4412_Oops_suspend NULL
- #define x4412_Oops_resume NULL
-
- static struct platform_driver x4412_Oops_driver = {
- .probe = x4412_Oops_probe,
- .remove = x4412_Oops_remove,
- .suspend = x4412_Oops_suspend,
- .resume = x4412_Oops_resume,
- .driver = {
- .name = "x4412-Oops",
- },
- };
-
- static struct platform_device x4412_Oops_device = {
- .name = "x4412-Oops",
- .id = -1,
- };
-
- static int __devinit x4412_Oops_init(void)
- {
- int ret;
-
- printk("x4412 Oops driver\r\n");
-
- ret = platform_device_register(&x4412_Oops_device);
- if(ret)
- printk("failed to register x4412 Oops device\n");
-
- ret = platform_driver_register(&x4412_Oops_driver);
- if(ret)
- printk("failed to register x4412 Oops driver\n");
-
- return ret;
- }
-
- static void x4412_Oops_exit(void)
- {
- platform_driver_unregister(&x4412_Oops_driver);
- }
-
- module_init(x4412_Oops_init);
- module_exit(x4412_Oops_exit);
-
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("lqm");
- MODULE_DESCRIPTION("x4412 Oops driver");
复制代码
本段程序使用了
sys
文件系统,在加载后,将会在
/sys/devices/platform
下产生
x4412-Oops
目录,对应
x4412_Oops
目录下会创立
Oops
文件,如下图所示:
当我们使用cat指令查看Oops内容时,将会触发x4412_Oops_read函数,使用echo指令给Oops填写内容时,将会触发x4412_Oops_write函数。在读写函数中,前面的程序清单中加粗标红的内容,将会访问0地址。
执行如下指令:
可以看到有如下打印信息出来:
- [root@x4412 x4412-Oops]# cat Oops
- [ 1569.887046] Unable to handle kernel NULL pointer dereference at virtual address 00000000
- [ 1569.894523] pgd = d6a90000
- [ 1569.896813] [00000000] *pgd=56f67831, *pte=00000000, *ppte=00000000
- [ 1569.902652] Internal error: Oops: 817 [#2] PREEMPT SMP
- [ 1569.907759] Modules linked in:
- [ 1569.910809] CPU: 0 Tainted: G D (3.0.15-9tripod #26)
- [ 1569.916909] PC is at x4412_Oops_read+0x18/0x20
- [ 1569.921317] LR is at dev_attr_show+0x28/0x50
- [ 1569.925570] pc : [<c022c3a4>] lr : [<c022e9b4>] psr: a0000013
- [ 1569.925594] sp : d6c73ed8 ip : d6c73ee8 fp : d6c73ee4
- [ 1569.937016] r10: bebbb988 r9 : 00001000 r8 : c05c5ef8
- [ 1569.942226] r7 : d6c73f70 r6 : d6c3ca80 r5 : d6937ea0 r4 : c0753afc
- [ 1569.948735] r3 : 00000000 r2 : 00000001 r1 : c0753afc r0 : 00000000
- ……
- [ 1570.824538] Backtrace:
- [ 1570.827017] [<c022c38c>] (x4412_Oops_read+0x0/0x20) from [<c022e9b4>] (dev_attr_show+0x28/0x50)
- [ 1570.835706] [<c022e98c>] (dev_attr_show+0x0/0x50) from [<c014d808>] (sysfs_read_file+0xa4/0x154)
- [ 1570.844420] r5:d6937ea0 r4:d6c3ca98
- [ 1570.848011] [<c014d764>] (sysfs_read_file+0x0/0x154) from [<c00fbbec>] (vfs_read+0xb4/0x148)
- [ 1570.856421] [<c00fbb38>] (vfs_read+0x0/0x148) from [<c00fbd5c>] (sys_read+0x44/0x74)
- [ 1570.864122] r8:00000000 r7:00000003 r6:00001000 r5:bebbb988 r4:d6c5b6e0
- [ 1570.870852] [<c00fbd18>] (sys_read+0x0/0x74) from [<c0051b40>] (ret_fast_syscall+0x0/0x30)
- [ 1570.879053] r9:d6c72000 r8:c0051ce8 r6:bebbb988 r5:00000003 r4:000aea20
- [ 1570.885754] Code: e24cb004 e3a03000 e3a02001 e1a00003 (e5832000)
- [ 1570.901538] ---[ end trace 7ced72ad36929528 ]---
- Segmentation fault
- [root@x4412 x4412-Oops]#
复制代码
以上一堆莫名其妙的信息,就是Oops信息。从第一行可以看到,程序出错的原因是因为访问了“Null pointer”。第七行给出了事发现场,即x4412_Oops_read函数偏离0x18个字节处。
我们可以通过反汇编来查询x4412_Oops_read函数偏离0x18个字节处对应的C代码。在ubuntu下进入对应的驱动生成目录:
- lqm@ubuntu:~/samba/x4412_ics_rtm_v10/kernel/drivers/char/oops$ ls
- built-in.o Kconfig Makefile modules.builtin modules.order x4412-oops.c x4412-oops.o
- lqm@ubuntu:~/samba/x4412_ics_rtm_v10/kernel/drivers/char/oops$
复制代码
执行如下指令可反汇编查询:
- lqm@ubuntu:~/samba/x4412_ics_rtm_v10/kernel/drivers/char/oops$ arm-none-linux-gnueabi-objdump -D x4412-oops.o
-
- x4412-oops.o: file format elf32-littlearm
- Disassembly of section .text:
-
- 00000000 <x4412_Oops_read>:
- 0: e1a0c00d mov ip, sp
- 4: e92dd800 push {fp, ip, lr, pc}
- 8: e24cb004 sub fp, ip, #4
- c: e3a03000 mov r3, #0
- 10: e3a02001 mov r2, #1
- 14: e1a00003 mov r0, r3
- 18: e5832000 str r2, [r3]
- 1c: e89da800 ldm sp, {fp, sp, pc}
-
- 00000020 <x4412_Oops_write>:
- 20: e1a0c00d mov ip, sp
- 24: e92dd800 push {fp, ip, lr, pc}
- 28: e24cb004 sub fp, ip, #4
- 2c: e3a03000 mov r3, #0
- 30: e3a02001 mov r2, #1
- 34: e1a00003 mov r0, r3
- 38: e5832000 str r2, [r3]
- 3c: e89da800 ldm sp, {fp, sp, pc}
- ……
- Disassembly of section .ARM.attributes:
-
- 00000000 <.ARM.attributes>:
- 0: 00002a41 andeq r2, r0, r1, asr #20
- 4: 61656100 cmnvs r5, r0, lsl #2
- 8: 01006962 tsteq r0, r2, ror #18
- c: 00000020 andeq r0, r0, r0, lsr #32
- 10: 412d3705 teqmi sp, r5, lsl #14
- 14: 070a0600 streq r0, [sl, -r0, lsl #12]
- 18: 12010841 andne r0, r1, #4259840 ; 0x410000
- 1c: 15011404 strne r1, [r1, #-1028] ; 0x404
- 20: 18031701 stmdane r3, {r0, r8, r9, sl, ip}
- 24: 1a011901 bne 46430 <dev_attr_Oops+0x46264>
- 28: Address 0x00000028 is out of bounds.
复制代码
这时我们可以很清楚的看到,x4412_Oops_read函数偏离0x18个字节处正好对应*p=1语句,和我们前面的假设不谋而合。
我们还可以通过反汇编看到,在x4412_Oops_write函数偏离0x38个字节处对应*p=1语句,我们不仿执行如下指令测试我们假设的正确性:
- [root@x4412 x4412-Oops]# echo 1 > Oops
复制代码
可以看到Oops部分信息如下:
- [ 1796.602205] Unable to handle kernel NULL pointer dereference at virtual address 00000000
- [ 1796.609818] pgd = d6b44000
- [ 1796.611964] [00000000] *pgd=56f2f831, *pte=00000000, *ppte=00000000
- [ 1796.617807] Internal error: Oops: 817 [#2] PREEMPT SMP
- [ 1796.622914] Modules linked in:
- [ 1796.625964] CPU: 0 Tainted: G D (3.0.15-9tripod #26)
- [ 1796.632064] PC is at x4412_Oops_write+0x18/0x20
- [ 1796.636559] LR is at dev_attr_store+0x24/0x28
- ……
- [ 1797.601235] Backtrace:
- [ 1797.603717] [<c022c3ac>] (x4412_Oops_write+0x0/0x20) from [<c022dfa0>] (dev_attr_store+0x24/0x28)
- [ 1797.612574] [<c022df7c>] (dev_attr_store+0x0/0x28) from [<c014d6e4>] (sysfs_write_file+0x104/0x184)
- [ 1797.621585] [<c014d5e0>] (sysfs_write_file+0x0/0x184) from [<c00fb998>] (vfs_write+0xb8/0x14c)
- [ 1797.630164] [<c00fb8e0>] (vfs_write+0x0/0x14c) from [<c00fbb08>] (sys_write+0x44/0x74)
- [ 1797.638042] r8:00000000 r7:00000004 r6:00000002 r5:000ae408 r4:d6894aa0
- [ 1797.644771] [<c00fbac4>] (sys_write+0x0/0x74) from [<c0051b40>] (ret_fast_syscall+0x0/0x30)
- [ 1797.653059] r9:d6916000 r8:c0051ce8 r6:4034cb40 r5:000ae408 r4:00000002
- [ 1797.659760] Code: e24cb004 e3a03000 e3a02001 e1a00003 (e5832000)
- [ 1797.674832] ---[ end trace 8cd1b48dca82dd13 ]---
复制代码
这里提示出错位置仍然在x4412_Oops_write函数偏移0x18处,从反汇编可以看到,执行*p=1的偏移位置,不正好是0x38-0x20=0x18处么?注意,这里0x20是x4412_Oops_write函数的起始偏移地址,因此必须减去该地址。
出现OOPS信息表面上是程序员不希望看到的,但是有时在调试过程中为了找问题,我们可以在疑似出错的位置加入以下语句故意抛出OOPS,以助于我们分析上下文信息。
1.1.1 利用strace
strace命令是linux平台下一种非常强大的调试工具,能够显示任何由用户空间发出的系统调用。strace显示这些调用的参数并返回符号形式的值。strace从内核接收信息,而且无需以任何特别的方式来构建内核。strace的每一行输出包括系统调用名称,参数和返回值。
以下是starce的一些参数说明:
l -c
:统计每一系统调用的所执行的时间、次数和出错的次数等;
l -d
:输出strace
关于标准错误的调试信息;
l -f
:跟踪由fork
调用所产生的子进程;
l -ff
:如果提供-o filename
,则所有进程的跟踪结果输出到相应的filename.pid
中,pid
是各进程的进程号;
l -F
:尝试跟踪vfork
调用。使用-f
时,vfork
将不被跟踪;
l -h
:输出简要的帮助信息;
l -i
:输出系统调用的入口指针;
l -q
:禁止输出关于脱离的消息;
l -r
:打印出相对时间关于每一个系统调用;
l -t
:在输出中的每一行前加上时间信息;
l -tt
:在输出中的每一行前加上时间信息,微秒级;
l -ttt
:微秒级输出,
以秒了表示时间;
l -T
:显示每一调用所耗的时间;
l -v
:输出所有的系统调用。一些调用关于环境变量、状态、输入输出等调用由于使用频繁,默认不输出;
l -V
:输出strace
的版本信息;
l -x
:以十六进制形式输出非标准字符串;
l -xx
:所有字符串以十六进制形式输出;
l -a column
:设置返回值的输出位置,默认为40
;
l -e expr
:指定一个表达式,用来控制如何跟踪。格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace、abbrev、verbose、raw、signal、read、write其中之一。value是用来限定的符号或数字。默认的 qualifier是 trace,感叹号是否定符号。例如-e open等价于-e trace=open,表示只跟踪open调用。而-e trace!=open表示跟踪除了open以外的其它调用。有两个特殊的符号 all 和 none,注意有些shell使用!来执行历史记录里的命令,所以要使用\\;
l -e trace=set
:只跟踪指定的系统调用。例如:-e trace=open
,close
,rean
,write
表示只跟踪这四个系统调用,默认的为set=all
;
l -e trace=file
:只跟踪有关文件操作的系统调用;
l -e trace=process
:只跟踪有关进程控制的系统调用;
l -e trace=network
:跟踪与网络有关的所有系统调用;
l -e strace=signal
:跟踪所有与系统信号有关的系统调用;
l -e trace=ipc
:跟踪所有与进程通讯有关的系统调用;
l -e abbrev=set
:设定strace
输出的系统调用的结果集。-v
等于abbrev=none
,默认为abbrev=all
;
l -e raw=set
:将指定的系统调用的参数以十六进制显示;
l -e signal=set
:指定跟踪的系统信号,默认为all
。如 signal=!SIGIO(
或者signal=!io),
表示不跟踪SIGIO
信号;
l -e read=set
:输出从指定文件中读出的数据,例如-e read=3
,5
;
l -e write=set
:输出写入到指定文件中的数据;
l -o filename
:将strace
的输出写入文件filename
;
l -p pid
:跟踪指定的进程pid
;
l -s strsize
:指定输出的字符串的最大长度,默认为32
,文件名一直全部输出;
l -u username
:以username
的UID
和GID
执行被跟踪的命令;
仅从上面的参数就可以知道,strace功能非常不简单,平时养成使用strace等工具调试驱动的好习惯,非常有助于加深对内核的理解。这里我们给出最简单的strace的使用方法,期望读者能够举一反三。
很可能默认构建的文件系统并不支持strace命令,我们需要将它手动移植到嵌入式平台上。有两种方法:其一就是使用传统的方法,从网上下载strace源码包,然后修改交叉编译工具链,再编译源码,调试,直到最终生成strace文件,生成之后,拷贝生成的文件,如果有依赖库,还需要将对应的库一并拷贝到指定目录。类似的移植案例太多了,如播放音视频需要移植mplayer,支持触摸屏需要移植tslib,抓取图像需要移植gsnap等,真正尝试过的都知道,少则两个小时,多则半天没有了。这里我们介绍第二种方法,使用强大的buildroot安装strace。
进入buildroot根目录,执行make menuconfig命令:
进入Targetpackages选项,
再进入Debugging,profilingand benchmark选项,
在里面找到strace
,选中,保存退出。然后在buildroot
根目录下执行make
,它将会自动从指定的服务器下载strace
源码,并自动指定我们配置好的交叉编译工具,自动编译,自动安装到根文件系统。整个过程不到五分钟搞定。如果执行make
时无法正常下载strace
源码,通常与网络质量或是服务器有关,一般多执行几次make
即可。
更新添加strace
的根文件系统,我们就可以使用它来测试了。例如我们可以使用如下指令测试蜂鸣器:
- echo 1 > sys/devices/platform/x4412-beep/state
复制代码
这时,蜂鸣器将会鸣叫。但是,在我们编写驱动时,可能它并不会像我们现在实验的那么顺利,这时我们就可以借助于strace
来分析问题了。当然这里只是一个实例,真正实际的场景可能比一个蜂鸣器驱动要复杂得多。
使用如下指令我们可以监控整个指令的执行过程:
- strace echo 1 > sys/devices/platform/x4412-beep/state
复制代码
打印信息如下:
- [root@x4412 x4412-beep]# strace echo 0 > state
- execve("/bin/echo", ["echo", "0"], [/* 19 vars */]) = 0
- brk(0) = 0xaf000
- uname({sys="Linux", node="x4412", ...}) = 0
- mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x400f8000
- access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
- open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
- fstat64(3, {st_mode=S_IFREG|0644, st_size=64, ...}) = 0
- mmap2(NULL, 64, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40156000
- close(3) = 0
- open("/lib/tls/v7l/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/v7l/neon/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/v7l/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/v7l/neon", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/v7l/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/v7l/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/v7l/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/v7l", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/neon/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/neon", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/tls", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/v7l/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/v7l/neon/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/v7l/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/v7l/neon", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/v7l/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/v7l/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/v7l/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/v7l", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/neon/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/neon", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
- stat64("/lib/vfp", 0xbef49210) = -1 ENOENT (No such file or directory)
- open("/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
- read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\34\204\1\0004\0\0\0"..., 512) = 512
- fstat64(3, {st_mode=S_IFREG|0755, st_size=1275392, ...}) = 0
- mmap2(NULL, 1312160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x401db000
- mprotect(0x4030e000, 32768, PROT_NONE) = 0
- mmap2(0x40316000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x133000) = 0x40316000
- mmap2(0x40319000, 9632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40319000
- close(3) = 0
- mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x400ce000
- set_tls(0x400ce4c0, 0x400ceba8, 0x400a3058, 0x400ce4c0, 0x400a3058) = 0
- mprotect(0x40316000, 8192, PROT_READ) = 0
- mprotect(0x400a2000, 4096, PROT_READ) = 0
- munmap(0x40156000, 64) = 0
- getuid32() = 0
- brk(0) = 0xaf000
- brk(0xd0000) = 0xd0000
- write(1, "0\n", 2) = 2
- exit_group(0) = ?
- +++ exited with 0 +++
- [root@x4412 x4412-beep]#
复制代码
1.1.1 利用hacking选项
内核开发者在make menuconfig的Kernelhacking提供了一些内核调试选项。这些选项有助于我们调试驱动程序,因为当我们启用某些调试选项的时候,操作系统会在发现驱动运行有问题时给出一些错误提示信息,而这些信息非常有助于驱动开发者找出驱动中的问题所在。
以下配置选项用于打开hacking相关调试信息:
l Generalsetup->Configure standard kernel features (expert users)-> Load allsymbols for debugging/ksymoops
l Kernelhacking — Kernel debugging
在kernel debugging中,又有多种选项,这里列举几个常用选项如下:
l Kerneldebugging->Debug shared IRQ handlers
可用于调试共享中断
l Kerneldebugging — Spinlock and rw-lock debugging: basic checks
可以检查到未初始化的自旋锁
l Kerneldebugging — Mutex debugging: basic checks
可以检查到未初始化的信号量
l Kerneldebugging — Spinlock debugging: sleep-inside-spinlock checking
可以检查出驱动在获取自旋锁后又睡眠以及死锁等状况
l MagicSysRq key
可以在已经死锁的情况下,打印一些有助于定位问题的信息
1.1.2 利用ioctl
驱动中的ioctl函数可以将驱动的一些信息返回给用户程序,也可以让用户程序通过ioctl系统调用设置一些驱动的参数。所以在驱动的开发过程中,可以扩展一些ioctl的命令用于传递和设置调试驱动时所需各种信息和参数,以达到调试驱动的目的。具体如何在驱动中实现ioctl,在后面的章节中将有详细的实例。
1.1.3 利用/proc 文件系统
Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件以及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
基于/proc文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这是因为他们都是动态创建的,而且可能随时会被刷新。
为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至子目录中,如/proc/scsi目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N中存储的则是系统当前正在运行的进程的相关信息,其中N为正在运行的进程(可以想象得到,在某进程结束后其相关目录则会消失)。
大多数虚拟文件可以使用cat、more或者less等命令进行查看,有些文件信息表述的内容可以一目了然,但也有文件的信息却不怎么具有可读性。不过,这些可读性较差的文件可以使用apm、free、lspci或top等查看。
/proc目录中包含许多以数字命名的子目录,这些数字表示系统当前正在运行进程的进程号,里面包含对应进程相关的多个信息文件。下面我们以QT测试程序为例,讲解这些文件的作用。
开发板进入系统后,在命令终端执行ps指令,可查看QT测试程序的PID:
- [root@x4412 proc]# ps
- PID USER COMMAND
- 1 root init
- 2 root [kthreadd]
- ……
- 1078 root /sbin/klogd -n
- 1120 root /usr/share/demo/qttest -qws
- 1121 root -sh
- 1164 root sh
- 1181 root ps
- [root@x4412 proc]#
复制代码
可以看出,QT测试程序qttest的PID号为1120,我们再进入/proc目录,可以找到1120目录,其内容如下:
- -r-------- 1 root root 0 Sep 25 06:32 auxv
- -r--r--r-- 1 root root 0 Sep 25 08:30 cgroup
- --w------- 1 root root 0 Sep 25 08:30 clear_refs
- -r--r--r-- 1 root root 0 Sep 25 08:29 cmdline
- -rw-r--r-- 1 root root 0 Sep 25 08:30 comm
- -rw-r--r-- 1 root root 0 Sep 25 08:30 coredump_filter
- lrwxrwxrwx 1 root root 0 Sep 25 08:30 cwd -> /
- -r-------- 1 root root 0 Sep 25 08:30 environ
- lrwxrwxrwx 1 root root 0 Sep 25 06:32 exe -> /usr/share/demo/qttest
- dr-x------ 2 root root 0 Sep 25 08:30 fd
- dr-x------ 2 root root 0 Sep 25 08:30 fdinfo
- -r--r--r-- 1 root root 0 Sep 25 08:30 limits
- -r--r--r-- 1 root root 0 Sep 25 08:30 maps
- -rw------- 1 root root 0 Sep 25 08:30 mem
- -r--r--r-- 1 root root 0 Sep 25 08:30 mountinfo
- -r--r--r-- 1 root root 0 Sep 25 08:30 mounts
- -r-------- 1 root root 0 Sep 25 08:30 mountstats
- dr-xr-xr-x 11 root root 0 Sep 25 08:30 net
- dr-x--x--x 2 root root 0 Sep 25 08:30 ns
- -rw-r--r-- 1 root root 0 Sep 25 08:30 oom_adj
- -r--r--r-- 1 root root 0 Sep 25 08:30 oom_score
- -rw-r--r-- 1 root root 0 Sep 25 08:30 oom_score_adj
- -r--r--r-- 1 root root 0 Sep 25 08:30 pagemap
- -r--r--r-- 1 root root 0 Sep 25 08:30 personality
- lrwxrwxrwx 1 root root 0 Sep 25 08:30 root -> /
- -r--r--r-- 1 root root 0 Sep 25 08:30 smaps
- -r--r--r-- 1 root root 0 Sep 25 08:30 stack
- -r--r--r-- 1 root root 0 Sep 25 08:29 stat
- -r--r--r-- 1 root root 0 Sep 25 08:30 statm
- -r--r--r-- 1 root root 0 Sep 25 08:30 status
- dr-xr-xr-x 3 root root 0 Sep 25 08:30 task
- -r--r--r-- 1 root root 0 Sep 25 08:30 wchan
复制代码
这里文件内容过多,我们挑一些关键的说明。
cmdline:启动当前进程的完整命令,但僵尸进程目录中的此文件不包含任何信息。使用cat cmdline指令测试结果如下:
- [root@x4412 1120]# cat cmdline
- /usr/share/demo/qttest-qws
- [root@x4412 1120]#
复制代码
上述结果表明,PID为1120的进程是由/usr/share/demo目录下的qttest-qws文件启动的。
cwd :指向当前进程运行目录的一个符号链接;
environ:当前进程的环境变量列表,彼此间用空字符(NULL)隔开;变量用大写字母表示,其值用小写字母表示。使用more environ指令测试结果如下:
- [root@x4412 1120]# more environ
- TERM=vt102SHELL=/bin/shTSLIB_CONFFILE=/etc/ts.confTSLIB_CALIBFILE=/etc/pointercalUSER=rootQWS_MOUSE_PROTO=tslib:/dev/input/event1 usb:/dev/input/event4TSLIB_TSEVENTTYPE=INPUTPATH=/sbin:/usr/sbin:/bin:/usr/binPWD=/HOME=/SHLVL=2TSLIB_TSDEVICE=/dev/input/event1_=/usr/share/demo/qttest
- [root@x4412 1120]#
复制代码
exe:指向启动当前进程的可执行文件(完整路径)的符号链接,通过/proc/N/exe可以启动当前进程的一个拷贝;
fd:这是个目录,包含当前进程打开的每一个文件的文件描述符(file descriptor),这些文件描述符是指向实际文件的一个符号链接;
- [root@x4412 1120]# ls fd -la
- total 0
- dr-x------ 2 root root 0 Sep 25 08:30 .
- dr-xr-xr-x 7 root root 0 Sep 25 06:32 ..
- lr-x------ 1 root root 64 Sep 25 08:35 0 -> /dev/null
- lrwx------ 1 root root 64 Sep 25 08:35 1 -> /dev/console
- lrwx------ 1 root root 64 Sep 25 08:35 10 -> /dev/input/event1
- lrwx------ 1 root root 64 Sep 25 08:35 11 -> /dev/tty0
- lrwx------ 1 root root 64 Sep 25 08:35 12 -> socket:[244]
- lrwx------ 1 root root 64 Sep 25 08:35 2 -> /dev/console
- lr-x------ 1 root root 64 Sep 25 08:35 3 -> pipe:[233]
- l-wx------ 1 root root 64 Sep 25 08:35 4 -> pipe:[233]
- lr-x------ 1 root root 64 Sep 25 08:35 5 -> pipe:[236]
- l-wx------ 1 root root 64 Sep 25 08:35 6 -> pipe:[236]
- lrwx------ 1 root root 64 Sep 25 08:35 7 -> socket:[238]
- lrwx------ 1 root root 64 Sep 25 08:35 8 -> /dev/fb0
- lrwx------ 1 root root 64 Sep 25 08:35 9 -> /dev/tty0
- [root@x4412 1120]#
复制代码
limits:当前进程所使用的每一个受限资源的软限制、硬限制和管理单元;此文件仅可由实际启动当前进程的UID用户读取。使用cat limits命令查询示例如下:
- [root@x4412 1120]# cat limits
- Limit Soft Limit Hard Limit Units
- Max cpu time unlimited unlimited seconds
- Max file size unlimited unlimited bytes
- Max data size unlimited unlimited bytes
- Max stack size 8388608 unlimited bytes
- Max core file size 0 unlimited bytes
- Max resident set unlimited unlimited bytes
- Max processes 5055 5055 processes
- Max open files 1024 4096 files
- Max locked memory 65536 65536 bytes
- Max address space unlimited unlimited bytes
- Max file locks unlimited unlimited locks
- Max pending signals 5055 5055 signals
- Max msgqueue size 819200 819200 bytes
- Max nice priority 0 0
- Max realtime priority 0 0
- Max realtime timeout unlimited unlimited us
- [root@x4412 1120]#
复制代码
maps:当前进程关联到的每个可执行文件和库文件在内存中的映射区域及其访问权限所组成的列表。使用cat maps命令查看的部分信息如下:
- [root@x4412 1120]# cat maps
- 00008000-0003f000 r-xp 00000000 b3:02 3020 /usr/share/demo/qttest
- 00047000-00048000 rw-p 00037000 b3:02 3020 /usr/share/demo/qttest
- 00048000-00100000 rw-p 00000000 00:00 0 [heap]
- 40010000-40012000 r-xp 00000000 b3:02 26 /usr/lib/libts-1.0.so.0.0.0
- 40012000-40019000 ---p 00002000 b3:02 26 /usr/lib/libts-1.0.so.0.0.0
- 40019000-4001a000 rw-p 00001000 b3:02 26 /usr/lib/libts-1.0.so.0.0.0
- 4001a000-4001b000 rw-p 00000000 00:00 0
- ……
- 40f0f000-40f12000 rw-p 00000000 00:00 0
- 40f12000-413c2000 rw-s 69ff4000 b3:02 5265 /dev/fb0
- 413c2000-413c7000 r-xp 00000000 b3:02 3314 /usr/lib/qt/plugins/imageformats/libqico.so
- 413c7000-413ce000 ---p 00005000 b3:02 3314 /usr/lib/qt/plugins/imageformats/libqico.so
- 413ce000-413cf000 rw-p 00004000 b3:02 3314 /usr/lib/qt/plugins/imageformats/libqico.so
- 413cf000-413d3000 r-xp 00000000 b3:02 37 /usr/lib/qt/plugins/imageformats/libqtga.so
- 413d3000-413da000 ---p 00004000 b3:02 37 /usr/lib/qt/plugins/imageformats/libqtga.so
- 413da000-413db000 rw-p 00003000 b3:02 37 /usr/lib/qt/plugins/imageformats/libqtga.so
- 413dd000-413de000 rw-s 00000000 00:04 0 /SYSV00000000 (deleted)
- ……
- be829000-be84a000 rw-p 00000000 00:00 0 [stack]
- ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
- [root@x4412 1120]#
复制代码
mem:当前进程所占用的内存空间,由open、read和lseek等系统调用使用,不能被用户读取;
root:指向当前进程运行根目录的符号链接。在Unix和Linux系统上,通常采用chroot命令使每个进程运行于独立的根目录;
stat:当前进程的状态信息,包含一系统格式化后的数据列,可读性差,通常由ps命令使用;
statm:当前进程占用内存的状态信息,通常以“页面”(page)表示;
status:与stat所提供信息类似,但可读性较好,如下所示,每行表示一个属性信息;
- [root@x4412 1120]# more status
- Name: qttest
- State: S (sleeping)
- Tgid: 1120
- Pid: 1120
- PPid: 1
- TracerPid: 0
- Uid: 0 0 0 0
- Gid: 0 0 0 0
- FDSize: 256
- Groups:
- VmPeak: 41112 kB
- VmSize: 36312 kB
- VmLck: 0 kB
- VmHWM: 27168 kB
- VmRSS: 27168 kB
- VmData: 20376 kB
- VmStk: 136 kB
- VmExe: 220 kB
- VmLib: 14600 kB
- VmPTE: 50 kB
- VmSwap: 0 kB
- Threads: 1
- SigQ: 0/5055
- SigPnd: 0000000000000000
- ShdPnd: 0000000000000000
- SigBlk: 0000000000000000
- SigIgn: 0000000000000006
- SigCgt: 0000000180015ee9
- CapInh: 0000000000000000
- CapPrm: ffffffffffffffff
- CapEff: ffffffffffffffff
- CapBnd: ffffffffffffffff
- Cpus_allowed: f
- Cpus_allowed_list: 0-3
- voluntary_ctxt_switches: 13424
- nonvoluntary_ctxt_switches: 3171
- [root@x4412 1120]#
复制代码
task:目录文件,包含由当前进程所运行的每一个线程的相关信息,每个线程的相关信息文件均保存在一个由线程号(tid)命名的目录中,这类似于每个进程目录中的内容;
/proc文件系统除了包含这种进程子目录外,还包含了很多其他有用的信息。这里我们介绍一些常用的文件。
/proc/buddyinfo:用于诊断内存碎片问题的相关信息文件;
/proc/cmdline:在启动时传递至内核的相关参数信息,使用cat指令测试如下:
- [root@x4412 proc]# cat cmdline
- console=ttySAC3,115200n8 androidboot.console=ttySAC3 root=/dev/mmcblk0p2 rw rootfstype=ext4 lcd=vs070cxn tp=ft5x06-1024x600 cam=ov2655 mac=00:09:c0:ff:ee:58
- [root@x4412 proc]#
复制代码
/proc/cpuinfo:处理器的相关信息的文件,使用cat指令查看当前信息如下:
- [root@x4412 proc]# cat cpuinfo
- Processor : ARMv7 Processor rev 0 (v7l)
- processor : 0
- BogoMIPS : 2991.71
-
- Features : swp half thumb fastmult vfp edsp neon vfpv3 tls
- CPU implementer : 0x41
- CPU architecture: 7
- CPU variant : 0x3
- CPU part : 0xc09
- CPU revision : 0
-
- Hardware : SMDK4X12
- Revision : 0000
- Serial : 0000000000000000
- [root@x4412 proc]#
复制代码
/proc/crypto:系统上已安装的内核使用的密码算法及每个算法的详细信息列表;
- [root@x4412 proc]# cat crypto
- name : ecb(arc4)
- driver : ecb(arc4-generic)
- module : kernel
- priority : 0
- refcnt : 1
- selftest : passed
- type : blkcipher
- blocksize : 1
- min keysize : 1
- max keysize : 256
- ivsize : 0
- geniv : <default>
- ……
复制代码
/proc/devices:系统已经加载的所有块设备和字符设备的信息,包含主设备号和设备组(与主设备号对应的设备类型)名;
- [root@x4412 proc]# more devices
- Character devices:
- 1 mem
- 4 /dev/vc/0
- 4 tty
- 4 ttyS
- 5 /dev/tty
- 5 /dev/console
- 5 /dev/ptmx
- ……
复制代码
/proc/diskstats:每块磁盘设备的磁盘I/O统计信息列表;
- [root@x4412 proc]# cat diskstats
- 1 0 ram0 0 0 0 0 0 0 0 0 0 0 0
- 1 1 ram1 0 0 0 0 0 0 0 0 0 0 0
- 1 2 ram2 0 0 0 0 0 0 0 0 0 0 0
- 1 3 ram3 0 0 0 0 0 0 0 0 0 0 0
- 1 4 ram4 0 0 0 0 0 0 0 0 0 0 0
- 1 5 ram5 0 0 0 0 0 0 0 0 0 0 0
- 1 6 ram6 0 0 0 0 0 0 0 0 0 0 0
- 1 7 ram7 0 0 0 0 0 0 0 0 0 0 0
- 1 8 ram8 0 0 0 0 0 0 0 0 0 0 0
- 1 9 ram9 0 0 0 0 0 0 0 0 0 0 0
- 1 10 ram10 0 0 0 0 0 0 0 0 0 0 0
- 1 11 ram11 0 0 0 0 0 0 0 0 0 0 0
- 1 12 ram12 0 0 0 0 0 0 0 0 0 0 0
- 1 13 ram13 0 0 0 0 0 0 0 0 0 0 0
- 1 14 ram14 0 0 0 0 0 0 0 0 0 0 0
- 1 15 ram15 0 0 0 0 0 0 0 0 0 0 0
- 7 0 loop0 0 0 0 0 0 0 0 0 0 0 0
- 7 1 loop1 0 0 0 0 0 0 0 0 0 0 0
- 7 2 loop2 0 0 0 0 0 0 0 0 0 0 0
- 7 3 loop3 0 0 0 0 0 0 0 0 0 0 0
- 7 4 loop4 0 0 0 0 0 0 0 0 0 0 0
- 7 5 loop5 0 0 0 0 0 0 0 0 0 0 0
- 7 6 loop6 0 0 0 0 0 0 0 0 0 0 0
- 7 7 loop7 0 0 0 0 0 0 0 0 0 0 0
- 179 0 mmcblk0 502 549 32744 465 115 273 778 2150 0 550 2615
- 179 1 mmcblk0p1 0 0 0 0 0 0 0 0 0 0 0
- 179 2 mmcblk0p2 501 549 32736 465 115 273 778 2150 0 550 2615
- 179 3 mmcblk0p3 0 0 0 0 0 0 0 0 0 0 0
- 179 4 mmcblk0p4 0 0 0 0 0 0 0 0 0 0 0
- [root@x4412 proc]#
复制代码
/proc/execdomains:内核当前支持的执行域信息列表;
- [root@x4412 proc]# more execdomains
- 0-0 Linux [kernel]
- [root@x4412 proc]#
复制代码
/proc/fb:帧缓冲设备列表文件,包含帧缓冲设备的设备号和相关驱动信息;
- [root@x4412 proc]# cat fb
- 0 s3cfb
- 1 s3cfb
- 2 s3cfb
- 3 s3cfb
- 4 s3cfb
- 10 s5ptvfb
- 10 s5ptvfb
- 10 s5ptvfb
- 10 s5ptvfb
- 10 s5ptvfb
- 10 s5ptvfb
- 11 s5ptvfb
- [root@x4412 proc]#
复制代码
/proc/filesystems:当前被内核支持的文件系统类型列表文件,被标示为nodev的文件系统表示不需要块设备的支持;通常mount一个设备时,如果没有指定文件系统类型将通过此文件来决定其所需文件系统的类型;
- [root@x4412 proc]# more filesystems
- nodev sysfs
- nodev rootfs
- nodev bdev
- nodev proc
- nodev cgroup
- nodev tmpfs
- nodev binfmt_misc
- nodev debugfs
- nodev sockfs
- nodev usbfs
- nodev pipefs
- nodev anon_inodefs
- nodev devpts
- ext2
- ext3
- ext4
- nodev ramfs
- vfat
- msdos
- nodev fuse
- fuseblk
- nodev fusectl
- [root@x4412 proc]#
复制代码
/proc/interrupts:IRQ相关的中断号列表;多路处理器平台上每个CPU对于每个I/O设备均有自己的中断号;
- [root@x4412 proc]# cat interrupts
- CPU0
- 28: 938 s3c-uart s5pv210-uart
- 30: 8294 s3c-uart s5pv210-uart
- 98: 0 GIC s3c-pl330.0
- 99: 0 GIC s3c-pl330.1
- 100: 0 GIC s3c-pl330.2
- 107: 0 GIC s3c2410-wdt
- 108: 0 GIC s3c2410-rtc alarm
- 121: 9 GIC mct_comp_irq
- 122: 103 GIC s3c2440-i2c.0
- 123: 7865 GIC s3c2440-i2c.1
- 124: 0 GIC s3c2440-i2c.2
- 125: 0 GIC s3c2440-i2c.3
- ……
复制代码
/proc/iomem:每个物理设备上的记忆体(RAM或者ROM)在系统内存中的映射信息;
/proc/kallsyms:模块管理工具用来动态链接或绑定可装载模块的符号定义,由内核输出;通常这个文件中的信息量相当大;
- [root@x4412 proc]# more kallsyms
- c0008000 T stext
- c0008000 T _sinittext
- c0008000 T _stext
- c0008000 T __init_begin
- c0008054 t __create_page_tables
- c0008108 t __enable_mmu_loc
- c0008114 t __fixup_pv_table
- c0008154 t __vet_atags
- c000818c t __fixup_smp
- c00081c4 t __fixup_smp_on_up
- ……
复制代码
/proc/kmsg:此文件用来保存由内核输出的信息,通常由/sbin/klogd或/bin/dmsg等程序使用,不要试图使用查看命令打开此文件;
/proc/loadavg:保存关于CPU和磁盘I/O的负载平均值,其前三列分别表示每1秒钟、每5秒钟及每15秒的负载平均值,类似于uptime命令输出的相关信息;第四列是由斜线隔开的两个数值,前者表示当前正由内核调度的实体(进程和线程)的数目,后者表示系统当前存活的内核调度实体的数目;第五列表示此文件被查看前最近一个由内核创建的进程的PID;
- [root@x4412 proc]# cat loadavg
- 2.00 2.01 1.97 1/54 1224
- [root@x4412 proc]#
- [root@x4412 proc]# uptime
- 10:19:05 up 3:46, load average: 2.00, 2.01, 1.96
- [root@x4412 proc]#
复制代码
/proc/meminfo:系统中关于当前内存的利用状况等的信息,常由free命令使用;可以使用文件查看命令直接读取此文件,其内容显示为两列,前者为统计属性,后者为对应的值;
- [root@x4412 proc]# more meminfo
- MemTotal: 647312 kB
- MemFree: 590592 kB
- Buffers: 1208 kB
- Cached: 15856 kB
- SwapCached: 0 kB
- Active: 24444 kB
- ……
复制代码
/proc/mounts:在内核2.4.29版本以前,此文件的内容为系统当前挂载的所有文件系统,在2.4.19以后的内核中引进了每个进程使用独立挂载名称空间的方式,此文件则随之变成了指向/proc/self/mounts(每个进程自身挂载名称空间中的所有挂载点列表)文件的符号链接;/proc/self是一个独特的目录,后文中会对此目录进行介绍;
- [root@x4412 proc]# ls mounts -la
- lrwxrwxrwx 1 root root 11 Sep 25 10:24 mounts -> self/mounts
复制代码
如下所示,其中第一列表示挂载的设备,第二列表示在当前目录树中的挂载点,第三点表示当前文件系统的类型,第四列表示挂载属性(ro或者rw),第五列和第六列用来匹配/etc/mtab文件中的转储(dump)属性;
- [root@x4412 proc]# more mounts
- rootfs / rootfs rw 0 0
- /dev/root / ext4 rw,relatime,barrier=1,data=ordered 0 0
- proc /proc proc rw,relatime 0 0
- devpts /dev/pts devpts rw,relatime,gid=5,mode=620 0 0
- tmpfs /dev/shm tmpfs rw,relatime,mode=777 0 0
- tmpfs /tmp tmpfs rw,relatime 0 0
- sysfs /sys sysfs rw,relatime 0 0
- [root@x4412 proc]#
复制代码
/proc/modules:当前装入内核的所有模块名称列表,可以由lsmod命令使用,也可以直接查看;如下所示,其中第一列表示模块名,第二列表示此模块占用内存空间大小,第三列表示此模块有多少实例被装入,第四列表示此模块依赖于其它哪些模块,第五列表示此模块的装载状态(Live:已经装入;Loading:正在装入;Unloading:正在卸载),第六列表示此模块在内核内存(kernel memory)中的偏移量;由于默认x4412开发板对应linux系统的所有驱动均编译进内核,因此这里我们查询时看不到任何的信息,如下所示:
- [root@x4412 proc]# more modules
- [root@x4412 proc]# lsmod
- Module Size Used by Not tainted
- [root@x4412 proc]#
- /proc/partitions:块设备每个分区的主设备号(major)和次设备号(minor)等信息,同时包括每个分区所包含的块(block)数目:
- [root@x4412 proc]# cat partitions
- major minor #blocks name
-
- 179 0 3866624 mmcblk0
- 179 1 1360260 mmcblk0p1
- 179 2 396742 mmcblk0p2
- 179 3 1575634 mmcblk0p3
- 179 4 396742 mmcblk0p4
- [root@x4412 proc]#
复制代码
/proc/stat:实时追踪自系统上次启动以来的多种统计信息;如下所示,其中,
“cpu”行后的八个值分别表示以1/100(jiffies)秒为单位的统计值(包括系统运行于用户模式、低优先级用户模式,运系统模式、空闲模式、I/O等待模式的时间等);
“intr”行给出中断的信息,第一个为自系统启动以来,发生的所有的中断的次数;然后每个数对应一个特定的中断自系统启动以来所发生的次数;
“ctxt”给出了自系统启动以来CPU发生的上下文交换的次数。
“btime”给出了从系统启动到现在为止的时间,单位为秒;
“processes”给出了自系统启动以来所创建的任务的个数目;
“procs_running”给出了当前运行队列的任务的数目;
“procs_blocked”给出了当前被阻塞的任务的数目;
- [root@x4412 proc]# more stat
- cpu 4048 0 2133 2131728 27 18 17 0 0 0
- cpu0 4043 0 2111 1476691 16 18 17 0 0 0
- intr 622690 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1582 0 14190 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 103 7865 0 0 0 0 0 0 0 0 0 0 1 57 0 0 0 0 0 2142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 511429 0 0 0 0 0 0 219 0 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- ctxt 1113684
- btime 1411626735
- processes 1240
- procs_running 1
- procs_blocked 0
- softirq 137981 5 63718 6 0 5 5 6756 1099 1349 65038
- [root@x4412 proc]#
复制代码
/proc/uptime:系统上次启动以来的运行时间,如下所示,其第一个数字表示系统运行时间,第二个数字表示系统空闲时间,单位是秒;
- [root@x4412 proc]# more uptime
- 15359.11 21838.53
- [root@x4412 proc]#
复制代码
/proc/version:当前系统运行的内核以及GCC版本号,如下所示;
- [root@x4412 proc]# more version
- Linux version 3.0.15-9tripod (lqm@ubuntu) (gcc version 4.4.3 (GCC) ) #26 SMP PREEMPT Wed Sep 24 04:40:23 PDT 2014
- [root@x4412 proc]#
复制代码
/proc/vmstat:当前系统虚拟内存的多种统计数据,信息量可能会比较大,这因系统而有所不同,可读性较好;
- [root@x4412 proc]# more vm
- vmallocinfo vmstat
- [root@x4412 proc]# more vmstat
- nr_free_pages 147652
- nr_inactive_anon 14
- nr_active_anon 5379
- nr_inactive_file 3516
- nr_active_file 733
- nr_unevictable 0
- nr_mlock 0
- nr_anon_pages 5378
- nr_mapped 1955
- ……
复制代码
/proc/zoneinfo:内存区域(zone)的详细信息列表,信息量较大,下面列出的是一个输出片段:
- [root@x4412 proc]# more zoneinfo
- Node 0, zone Normal
- pages free 83623
- min 864
- low 1080
- high 1296
- scanned 0
- spanned 188416
- present 186944
- nr_free_pages 83623
- nr_inactive_anon 0
- nr_active_anon 0
- nr_inactive_file 105
- ……
复制代码
/proc/sys:与/proc下其它文件的只读属性不同的是,管理员可对/proc/sys子目录中的许多文件内容进行修改以更改内核的运行特性,事先可以使用“ls –l”命令查看某文件是否可写入。写入操作通常使用类似于“echo DATA > $yourfilename”的格式进行。需要注意的是,即使文件可写,其一般也不可以使用编辑器进行编辑。
这里给出/proc文件系统的一些常用文件介绍,后面我们将会给出更加详细的创建/proc节点调试内核的示例教程。
1.1.4 使用kgdb
kgdb是在内核源码中打用于调试内核的补丁,然后通过相应的硬件和软件,就可以像gdb单步调试应用程序一样来调试内核和驱动了。