嵌入式 kernel调试方法小结
2019-07-12 19:09 发布
生成海报
kdb:只能在汇编代码级进行调试;
优点是不需要两台机器进行调试。
gdb:在调试模块时缺少一些至关重要的功能,它可用来查看内核的运行情况,包括反汇编内核函数。
kgdb:能很方便的在源码级对内核进行调试,缺点是kgdb只能进行远程调试,它需要一根串口线及两台机器来调试内核(也可以是在同一台主机上用vmware软件运行两个操作系统来调试)
printk() 是调试内核代码时最常用的一种技术。在内核代码中的特定位置加入printk() 调试调用,可以直接把所关心的信息打打印到屏幕上,从而可以观察程序的执行路径和所关心的变量、指针等信息。 Linux 内核调试器(Linux kernel debugger,kdb)是 Linux 内核的补丁,它提供了一种在系统能运行时对内核内存和数据结构进行检查的办法。Oops、KDB在文章掌握 Linux
调试技术有详细介绍,大家可以参考。 Kprobes 提供了一个强行进入任何内核例程,并从中断处理器无干扰地收集信息的接口。使用 Kprobes 可以轻松地收集处理器寄存器和全局数据结构等调试信息,而无需对Linux内核频繁编译和启动,具体使用方法,请参考使用 Kprobes 调试内核。
/proc文件系统
在 /proc 文件系统中,对虚拟文件的读写操作是一种与内核通信的手段,要查看内核回环缓冲区中的消息,可以使用 dmesg 工具(或者通过 /proc 本身使用 cat /proc/kmsg 命令)。清单 6 给出了 dmesg 显示的最后几条消息。
清单 6. 查看来自 LKM 的内核输出
[ root@ plato] # dmesg | tail -5
cs: IO port probe 0xa00-0xaff: clean.
eth0: Link is down
eth0: Link is up, running at 100Mbit half-duplex
my_module_init called. Module is now loaded.
my_module_cleanup called. Module is now unloaded.
可以在内核输出中看到这个模块的消息。现在让我们暂时离开这个简单的例子,来看几个可以用来开发有用 LKM 的内核 API。
调试工具
使用调试器来一步步地跟踪代码,查看变量和计算机寄存器的值。在内核中使用交互式调试器是一个很复杂的问题。内核在它自己的地址空间中运行。许多用户空间下的调试器所提供的常用功能很难用于内核之中,比如断点和单步调试等。
目录
[ 隐藏 ]
内核bug跟踪
oops消息分析
(1)oops消息产生机制
oops(也称 panic),称程序运行崩溃,程序崩溃后会产生oops消息。应用程序或内核线程的崩溃都会产生oops消息,通常发生oops时,系统不会发生死机,而在终端或日志中打印oops信息。
当使用NULL指针或不正确的指针值时,通常会引发一个 oops 消息,这是因为当引用一个非法指针时,页面映射机制无法将虚拟地址映像到物理地址,处理器就会向操作系统发出一个"页面失效"的信号。内核无法"换页"到并不存在的地址上,系统就会产生一个"oops"。
oops 显示发生错误时处理器的状态,包括 CPU 寄存器的内容、页描述符表的位置,以及其一些难理解的信息。这些消息由失效处理函数(arch/*/kernel/traps.c)中的printk 语句产生。较为重要的信息就是指令指针(EIP),即出错指令的地址。
由于很难从十六进制数值中看出含义,可使用符号解析工具klogd。klogd 守护进程能在 oops 消息到达记录文件之前对它们解码。klogd在缺省情况下运行并进行符号解码。
通常Oops文本由klogd从内核缓冲区里读取并传给syslogd,由syslogd写到syslog文件中,该文件典型为/var/log/messages(依赖于/etc/syslog.conf)。如果klogd崩溃了,用户可"dmesg > file"从内核缓冲区中读取数据并保存下来。还可用"cat /proc/kmsg > file"读取数据,此时,需要用户中止传输,因为kmsg是一个"永不结束的文件"。
当保护错误发生时,klogd守护进程自动把内核日志信息中的重要地址翻译成它们相应的符号。klogd执行静态地址翻译和动态地址翻译。静态地址翻译使用System.map文件将符号地址翻译为符号。klogd守护进程在初始化时必须能找到system.map文件。
动态地址翻译通常对内核模块中的符号进行翻译。内核模块的内存从内核动态内存池里分配,内核模块中符号的位置在内核装载后才最终确定。
Linux内核提供了调用,允许程序决定装载哪些模块和它们在内存中位置。通过这些系统调用,klogd守护进程生成一张符号表用于调试发生在可装载模块中的保护错误。内核模块的装载或者卸载都会自动向klogd发送信号,klogd可将内核模块符号的地址动态翻译为符号字符串。
(2)产生oops的样例代码
使用空指针和缓冲区溢出是产生oops的两个最常见原因。下面两个函数faulty_write和faulty_read是一个内核模块中的写和读函数,分别演示了这两种情况。当内核调用这两个函数时,会产生oops消息。
函数faulty_write删除一个NULL指针的引用,由于0不是一个有效的指针值,内核将打印oops信息,并接着,杀死调用些函数的进程。
ssize_t faulty_write ( struct file * filp, const char _ _user * buf, size_t count, loff_t * pos)
{
/* make a simple fault by dereferencing a NULL pointer */
* ( int * ) 0 = 0 ;
return 0 ;
}
函数faulty_write产生oops信息列出如下(注意 EIP 行和 stack 跟踪记录中已经解码的符号):
Unable to handle kernel NULL pointer dereference at virtual address
00000000
printing eip: c48370c3
* pde = 00000000 Oops: 0002 CPU: 0 EIP: 0010:[ faulty:faulty_write+3 / 576 ] EFLAGS: 00010286 eax: ffffffea
ebx: c2c55ae0 ecx: c48370c0 edx: c2c55b00 esi: 0804d038 edi: 0804d038 ebp: c2337f8c esp: c2337f8c ds: 0018 es: 0018 ss: 0018 Process
cat ( pid: 23413 ,
stackpage =c2337000) Stack: 00000001 c01356e6 c2c55ae0 0804d038 00000001 c2c55b00 c2336000
00000001
0804d038 bffffbd4 00000000 00000000 bffffbd4 c010b860 00000001
0804d038
00000001 00000001 0804d038 bffffbd4 00000004 0000002b 0000002b
00000004
Call Trace:
[ sys_write+214 / 256 ]
[ system_call+52 / 56 ]
Code: c7 05 00 00 00 00 00 00 00 00 31 c0 89 ec 5d c3 8d b6 00 00
上述oops消息中,字符串 3/576 表示处理器正处于函数的第3个字节上,函数整体长度为 576 个字节。 函数faulty_read拷贝一个字符串到本地变量,由于字符串比目的地数组长造成缓冲区溢出。当函数返回时,缓冲区溢出导致产生oops信息。因为返回指令引起指令指针找不到运行地址,这种错误很难发现和跟踪。
ssize_t faulty_read( struct file * filp, char _ _user * buf, size_t count, loff_t * pos)
{
int ret;
char stack_buf[ 4 ] ;
/* Let's try a buffer overflow */
memset( stack_buf, 0xff , 20 ) ;
if ( count > 4 )
count = 4 ;
/* copy 4 bytes to the user */
ret = copy_to_user( buf, stack_buf, count) ;
if ( ! ret)
return count;
return ret;
}
函数faulty_read产生oops信息列出如下:
EIP: 0010:[ < 00000000> ]
Unable to handle kernel paging request at virtual address ffffffff printing eip: ffffffff Oops: 0000
[ #5] SMP CPU:
0 EIP: 0060:[ ] Not tainted EFLAGS: 00010296
( 2.6.6) EIP is at 0xffffffff eax: 0000000c ebx: ffffffff ecx: 00000000 edx: bfffda7c esi: cf434f00 edi: ffffffff ebp: 00002000 esp: c27fff78 ds: 007b es: 007b ss: 0068 Process
head ( pid: 2331 ,
threadinfo =c27fe000 task =c3226150) Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7 bfffda70 c27fe000 c0150612 cf434f00 bfffda70 00002000 cf434f20 00000000
00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70 Call Trace:
[ ] sys_read+0x42/ 0x70
[ ] syscall_call+0x7/ 0xb
Code: Bad EIP value.
在上述oops消息中,由于缓冲区溢出,仅能看到函数调用栈的一部分,看不见函数名vfs_read和faulty_read,并且代码(Code)处仅输出"bad EIP value.",列在栈上开始处的地址"ffffffff"表示内核栈已崩溃。
(3)oops信息分析
面对产生的oops信息,首先应查找源程序发生oops的位置,通过查看指令指令寄存器EIP的值,可以找到位置,如:EIP: 0010:[faulty:faulty_write+3/576]。
再查找函数调用栈(call stack)可以得到更多的信息。从函数调用栈可辨别出局部变量、全局变量和函数参数。例如:在函数faulty_read的oops信息的函数调用栈中,栈顶为ffffffff,栈顶值应为一个小于ffffffff的值,为此值,说明再找不回调用函数地址,说明有可能因缓冲区溢出等原因造成指针错误。
在x86构架上,用户空间的栈从0xc0000000以下开始,递归值bfffda70可能是用户空间的栈地址。实际上它就是传递给read系统调用的缓冲区地址,系统调用read进入内核时,将用户空间缓冲区的数据拷贝到内核空间缓冲区。
如果oops信息显示触发oops的地址为0xa5a5a5a5,则说明很可能是因为没有初始化动态内存引起的。
另外,如果想看到函数调用栈的符号,编译内核时,请打开CONFIG_KALLSYMS选项。
klogd 提供了许多信息来帮助分析。为了使 klogd 正确地工作,必须在 /boot 中提供符号表文件 System.map。如果符号表与当前内核不匹配,klogd 就会拒绝解析符号。
有时内核错误会将系统完全挂起。例如代码进入一个死循环,系统不会再响应任何动作。这时可通过在一些关键点上插入 schedule 调用可以防止死循环。
系统崩溃重启动
由于内核运行错误,在某些极端情况下,内核会运行崩溃,内核崩溃时会导致死机。为了解决此问题,内核引入了快速装载和重启动新内核机制。内核通过kdump在崩溃时触发启动新内核,存储旧内存映像以便于调试,让系统在新内核上运行 ,从而避免了死机,增强了系统的稳定性。
(1)工具kexec介绍
kexec是一套系统调用,允许用户从当前正执行的内核装载另一个内核。用户可用shell命令"yum install kexec-tools"安装kexec工具包,安装后,就可以使用kexec命令。
工具kexec直接启动进入一个新内核,它通过系统调用使用户能够从当前内核装载并启动进入另一个内核。在当前内核中,kexec执行BootLoader的功能。在标准系统启动和kexec启动之间的主要区别是:在kexec启动期间,依赖于硬件构架的固件或BIOS不会被执行来进行硬件初始化。这将大大降低重启动的时间。
为了让内核的kexec功能起作用,内核编译配置是应确认先择了"CONFIG_KEXEC=y",在配置后生成的.config文件中应可看到此条目。
工具kexec的使用分为两步,首先,用kexec将调试的内核装载进内存,接着,用kexec启动装载的内核。
装载内核的语法列出如下:
kexec -l kernel-image --append=command-line-options --initrd=initrd-image
上述命令中,参数kernel-image为装载内核的映射文件,该命令不支持压缩的内核映像文件bzImage,应使用非压缩的内核映射文件vmlinux;参数initrd-image为启动时使用initrd映射文件;参数command-line-options为命令行选项,应来自当前内核的命令行选项,可从文件"/proc/cmdline"中提取,该文件的内容列出如下:
^-^$ cat /proc/cmdline
ro root=/dev/VolGroup00/LogVol00 rhgb quiet
例如:用户想启动的内核映射为/boot/vmlinux,initrd为/boot/initrd,则kexec加载命令列出如下:
Kexec –l /boot/vmlinux –append=/dev/VolGroup00/LogVol00 initrd=/boot/initrd
还可以加上选项-p或--load-panic,表示装载新内核在系统内核崩溃使用。
在内核装载后,用下述命令启动装载的内核,并进行新的内核中运行:
kexec -e
当kexec将当前内核迁移到新内核上运行时,kexec拷贝新内核到预保留内存块,该保留位置如图1所示, 原系统内核给kexec装载内核预保留一块内存(在图中的阴影部分),用于装载新内核,其他内存区域在未装载新内核时,由原系统内核使用。
图1 kexec装载的内核所在预保留位置示意图
在x86构架的机器上,系统启动时需要使用第一个640KB物理内存,用于内核装载,kexec在重启动进入转储捕捉的内核之前备份此区域。相似地,PPC64构架的机器在启动里需要使用第一个32KB物理内核,并需要支持64K页,kexec备份第一个64KB内存。
(2)kdump介绍
kdump是基于kexec的崩溃转储机制(kexec-based Crash Dumping),无论内核内核需要转储时,如:系统崩溃时,kdump使用kexec快速启动进入转储捕捉的内核。在这里,原运行的内核称为系统内核或原内核,新装载运行的内核称为转储捕捉的内核或装载内核或新内核。
在重启动过程中,原内核的内存映像被保存下来,并且转储捕捉的内核(新装载的内核)可以访问转储的映像。用户可以使用命令cp和scp将内存映射拷贝到一个本地硬盘上的转储文件或通过网络拷贝到远程计算机上。
当前仅x86, x86_64, ppc64和ia64构架支持kdump和kexec。
当系统内核启动时,它保留小部分内存给转储(dump)捕捉的内核,确保了来自系统内核正进行的直接内存访问(Direct Memory Access:DMA)不会破坏转储捕捉的内核。命令kexec –p装载新内核到这个保留的内存。
在崩溃前,所有系统内核的核心映像编码为ELF格式,并存储在内核的保留区域。ELF头的开始物理地址通过参数elfcorehdr=boot传递到转储捕捉的内核。
通过使用转储捕捉的内核,用户可以下面两种方式访问内存映像或旧内存:
(1)通过/dev/oldmem设备接口,捕捉工具程序能读取设备文件并以原始流的格式写出内存,它是一个内存原始流的转储。分析和捕捉工具必须足够智能以判断查找正确信息的位置。
(2)通过/proc/vmcore,能以ELF格式文件输出转储信息,用户可以用GDB(GNU Debugger)和崩溃调试工具等分析工具调试转储文件。
(3)建立快速重启动机制和安装工具
1)安装工具kexec-tools
可以下载源代码编译安装工具kexec-tools。由于工具kexec-tools还依赖于一些其他的库,因此,最好的方法是使用命令"yum install kexec-tools"从网上下载安装并自动解决依赖关系。
2)编译系统和转储捕捉的内核
可编译独立的转储捕捉内核用于捕捉内核的转储,还可以使用原系统内核作为转储捕捉内核,在这种情况下,不需要再编译独立的转储捕捉内核,但仅支持重定位内核的构架才可以用作转储捕捉的内核,如:构架i386和ia64支持重定位内核。
对于系统和转储捕捉内核来说,为了打开kdump支持,内核需要设置一些特殊的配置选项,下面分别对系统内核和转储捕捉内核的配置选项进行说明:
系统内核的配置选项说明如下:
在菜单条目"Processor type and features."中打开选项"kexec system call",使内核编译安装kexe系统调用。配置文件.config生成语句"CONFIG_KEXEC=y"。 在菜单条目"Filesystem"->"Pseudo filesystems."中打开选项"sysfs file system support",使内核编译安装文件系统sysfs.配置文件.config生成语句"CONFIG_SYSFS=y"。 在菜单条目"Kernel hacking."中打开选项"Compile the kernel with debug info ",使内核编译安装后支持调试信息输出,产生调试符号用于分析转储文件。配置文件.config生成语句"CONFIG_DEBUG_INFO=Y"。 转储捕捉内核配置选项(不依赖于处理器构架)说明如下:
在菜单条目"Processor type and features"中打开选项"kernel crash dumps",配置文件.config生成语句" CONFIG_CRASH_DUMP=y"。 在菜单条目"Filesystems"->"Pseudo filesystems"中打开选项"/proc/vmcore support",配置文件.config生成语句"CONFIG_PROC_VMCORE=y"。 转储捕捉内核配置选项(依赖于处理器构架i386和x86_64)说明如下:
在处理器构架i386上,在菜单条目"Processor type and features"中打开高端内存支持,配置文件.config生成语句"CONFIG_HIGHMEM64G=y"或"CONFIG_HIGHMEM4G"。 在处理器构架i386和x86_64上,在菜单条目"rocessor type and features"中关闭对称多处理器支持,配置文件.config生成语句"CONFIG_SMP=n"。如果配置文件中的设置为"CONFIG_SMP=y",则可在装载转储捕捉内核的内核命令行上指定"maxcpus=1"。 如果想构建和使用可重定位内核,在菜单条目"rocessor type and featuresIf"中打开选项"Build a relocatable kernel",配置文件.config生成语句"CONFIG_RELOCATABLE=y"。 在菜单"Processor type and features"下的条目"Physical address where the kernel is loaded"设置合适的值用于内核装载的物理地址。它仅在打开了"kernel crash dumps"时出现。合适的值依赖于内核是否可重定位。 如果设置了值"CONFIG_PHYSICAL_START=0x100000",则表示使用可重定位内核。它将编译内核在物理地址1MB处,内核是可重定位的,因此,内核可从任何物理地址运行。Kexec BootLoader将装载内核到用于转储捕捉内核的内核保留区域。
否则,将使用启动参数"crashkernel=Y@X"指定第二个内核保留内核区域的开始地址,其中,Y表示内存区域的大小,X表示保留给转储捕捉内核的内存区域的开始地址,通过X为16MB (0x1000000),因此用户可设置"CONFIG_PHYSICAL_START=0x1000000"。
在配置完内核后,编译和安装内核及内核模块。
3)扩展的crashkernel语法
在系统内核的启动命令行选项中,通常语法"crashkernel=size[@offset]"对于大多数据配置已够用了,但有时候保留的内存依赖于系统RAM。此时可通过扩展的crashkernel命令行对内存进行 限制避免从机器上移去一部分内核后造成系统不可启动。扩展的crashkernel语法列出如下:
crashkernel =< range1> :< size1> [ ,< range2> :< size2> ,...] [ @ offset]
其中,range=start-[end]。
例如:crashkernel=512M-2G:64M,2G-:128M,含义为:如果内存小于512M,不设置保留内存,如果内存为512M到2G之间,设置保留内存区域为64M,如果内存大于128M,设置保留内存区域为128M。
4)启动进入系统内核
必要时更新BootLoader。然后用参数"crashkernel=Y@X"启动系统内核,如:crashkernel=64M@16M,表示告诉系统内核保留从物理地址0x01000000 (16MB)开始的64MB大小给转储捕捉内核使用。通常x86和x86_64平台设置"crashkernel=64M@16M",ppc64平台设置"crashkernel=128M@32M"。
5)装载转储捕捉内核
在启动进入系统内核后,需要装载转储捕捉内核。根据处理器构架和映射文件的类型(可否重定位),可以选择装载不压缩的vmlinux或压缩的bzImage/vmlinuz内核映像。选择方法说明如下:
对于i386和x86_64平台:
如果内核不是可重定位的,使用vmlinux。 如果内核是可重定位的,使用bzImage/vmlinuz。 对于ppc64平台:
对于ia64平台:
如果用户使用不压缩的vmlinux映像,那么使用下面的命令装载转储捕捉内核:
kexec -p < dump-capture-kernel-vmlinux-image>
--initrd =< initrd-for-dump-capture-kernel> --args-linux
--append ="root= "
如果用户使用压缩的bzImage/vmlinuz映像,那么使用下面的命令装载转储捕捉内核:
kexec -p < dump-capture-kernel-bzImage>
--initrd =< initrd-for-dump-capture-kernel>
--append ="root= "
注意:参数--args-linux在ia64平台中不用指定。
下面是在装载转储捕捉内核时使用的构架特定命令行选项:
对于i386, x86_64和ia64平台,选项为"1 irqpoll maxcpus=1 reset_devices"。 对于ppc64平台,选项为"1 maxcpus=1 noirqdistrib reset_devices"。 在装载转储捕捉内核时需要注意的事项说明如下:
缺省设置下,ELF头以ELF64格式存储,以支持多于4GB内核的系统,在i386上,kexec自动检查物理RAM尺寸是否超过4GB限制,如果没有超过,使用ELF32。因此,在非PAE系统上ELF头总是使用ELF32格式。 选项--elf32-core-headers可用于强制产生ELF32头,这是必要的,因为在32位系统上,GDB当前不能打开带有ELF64头的vmcore文件。 在转储捕捉内核中,启动参数irqpoll减少了由于共享中断引起的驱动程序初始化失败。 用户必须以命令mount输出的根设备名的格式指定。 启动参数"1"将转储捕捉内核启动进入不支持网络的单用户模式。如果用户想使用网络,需要设置为3。 通常不必让转储捕捉内核以SMP方式运行。因此,通常编译一个单CPU转储捕捉内核或装载转储捕捉内核时指定选项"maxcpus=1"。 6)内核崩溃时触发内核启动
在装载转储捕捉内核后,如果系统发生崩溃(Kernel Panic),系统将重启动进入转储捕捉内核。重启动的触发点在函数die(), die_nmi()和sysrq处理例程(按ALT-SysRq-c组合键)。
下面条件将执行一个崩溃触发点:
如果检测到硬件锁住,并且配置了"NMI watchdog",系统将调用函数die_nmi()启动进入转储捕捉内核。 如果调用了函数die(),并且该线程的pid为0或1,或者在中断上下文中调用die(),或者设置了panic_on_oops并调用了die(),系统将启动进入转储捕捉内核。 在powerpc系统,当一个软复位产生时,所有的CPU调用die(),并且系统将启动进入转储捕捉内核。 为了测试目的,用户可以使用"ALT-SysRq-c","echo c > /proc/sysrq-trigger"触发一个崩溃,或者写一个内核模块强制内核崩溃。 7)写出转储文件
在转储捕捉内核启动后,可用下面的命令写出转储文件:
cp /proc/vmcore
用户还可以将转储内存作为设备/dev/oldmem以线性原始流视图进行访问,使用下面的命令创建该设备:
mknod /dev/oldmem c 1 12
使用命令dd拷贝转储内存的特定部分,拷贝整个内存的命令列出如下:
dd if=/dev/oldmem of=oldmem.001
8)转储文件分析
在分析转储映像之前,用户应重启动进入一个稳定的内核。用户可以用GDB对拷贝出的转储进行有限分析。编译vmlinux时应加上-g选项,才能生成调试用的符号,然后,用下面的命令调试vmlinux:
gdb vmlinux < dump-file>
SysRq魔术组合键打印内核信息
SysRq"魔术组合键"是一组按键,由键盘上的"Alt+SysRq+[CommandKey]"三个键组成,其中CommandKey为可选的按键。SysRq魔术组合键根据组合键的不同,可提供控制内核或打印内核信息的功能。SysRq魔术组合键的功能说明如表1所示。
表1 SysRq组合键的功能说明
键名
功能说明
b
在没有同步或卸载硬盘的情况下立即启动。
c
为了获取崩溃转储执行kexe重启动。
d
显示被持的所有锁。
e
发送信号SIGTERM给所有进程,除了init外。
f
将调用oom_kill杀死内存热进程。
g
在平台ppc和sh上被kgdb使用。
h
显示帮助信息。
i
发送信号SIGKILL给所有的进程,除了init外。
k
安全访问密钥(Secure Access Key,SAK)杀死在当前虚拟终端上的所有程序。
m
转储当前的内存信息到控制台。
n
用于设置实时任务为可调整nice的。
o
将关闭系统(如果配置为支持)。
p
打印当前寄存器和标识到控制台。
q
将转储所有正运行定时器的列表。
r
关闭键盘Raw模式并设置为XLATE模式。
s
尝试同步所有挂接的文件系统。
t
将转储当前的任务列表和它们的信息到控制台。
u
尝试以仅读的方式重挂接所有已挂接的文件系统。
v
转储Voyager SMP处理器信息到控制台。
w
转储的所有非可中断(已阻塞)状态的任务。
x
在平台ppc/powerpc上被xmon(X监视器)接口使用。
0~9
设备控制台日志级别,控制将打印到控制台的内核信息。例如:0仅打印紧急信息,如:PANIC和OOPS信息。
默认SysRq组合键是关闭的。可用下面的命令打开此功能:
# echo 1 > /proc/sys/kernel/sysrq
关闭此功能的命令列出如下:
# echo 0 > /proc/sys/kernel/sysrq
如果想让此功能总是起作用,可在/etc/sysctl.conf文件中设置kernel.sysrq值为1。 系统重新启动以后,此功能将会自动打开。
打开SysRq组合键功能后,有终端访问权限的用户就可以自用它打印内核信息了。
注意:SysRq组合键在X windows上是无法使用的。必须先要切换到文本虚拟终端下。如果在图形界面,可以按Ctrl+Alt+F1切换到虚拟终端。在串口终端上,需要先在终端上发送Break信号,然后在5秒内输入sysrq组合键。如果用户有root权限,可把commandkey字符写入到/proc/sysrq-trigger文件,触发一个内核信息打印,打印的信息存放在/var/log/messages中。下面是一个命令样例:
^-^$ echo 't' > sysrq-trigger
^-^vim / var/ log/ messages
Oct 29 17 :51 :43 njllinux kernel: SysRq : Show State
Oct 29 17 :51 :43 njllinux kernel: task PC stack pid father
Oct 29 17 :51 :43 njllinux kernel: init S ffffffff812b76a0 0 1 0
Oct 29 17 :51 :43 njllinux kernel: ffff81013fa97998 0000000000000082 0000000000000000 ffff81013fa9795c
Oct 29 17 :51 :43 njllinux kernel: 000000003fa97978 ffffffff81583700 ffffffff81583700 ffff81013fa98000
Oct 29 17 :51 :43 njllinux kernel: ffffffff813cc5b0 ffff81013fa98350 000000003c352a50 ffff81013fa98350
Oct 29 17 :51 :43 njllinux kernel: Call Trace:
Oct 29 17 :51 :43 njllinux kernel: 000300000004 ffff8101333cb090
Oct 29 17 :51 :43 njllinux kernel: Call Trace:
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff81040c2e> ] sys_pause+0x19/ 0x22
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff8100c291> ] tracesys+0xd0/ 0xd5
Oct 29 17 :51 :43 njllinux kernel:
Oct 29 17 :51 :43 njllinux kernel: lighttpd S ffffffff812b76a0 0 3365 1
Oct 29 17 :51 :43 njllinux kernel: ffff810132d49b18 0000000000000082 0000000000000000 ffff810132d49adc
Oct 29 17 :51 :43 njllinux kernel: ffff81013fb2d148 ffffffff81583700 ffffffff81583700 ffff8101354896a0
Oct 29 17 :51 :43 njllinux kernel: ffffffff813cc5b0 ffff8101354899f0 0000000032d49ac8 ffff8101354899f0
Oct 29 17 :51 :43 njllinux kernel: Call Trace:
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff81040722> ] ? __mod_timer+0xbb/ 0xcd
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff8129b2ee> ] schedule_timeout+0x8d/ 0xb4
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff81040100> ] ? process_timeout+0x0/ 0xb
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff8129b2e9> ] ? schedule_timeout+0x88/ 0xb4
Oct 29 17 :51 :43 njllinux kernel: [ < ffffffff810b9498> ] do_sys_poll+0x2a8/ 0x370
……
命令strace
命令strace 显示程序调用的所有系统调用。使用 strace 工具,用户可以清楚地看到这些调用过程及其使用的参数,了解它们与操作系统之间的底层交互。当系统调用失败时,错误的符号值(如 ENOMEM)和对应的字符串(如Out of memory)都能被显示出来。
starce 的另一个用处是解决和动态库相关的问题。当对一个可执行文件运行ldd时,它会告诉你程序使用的动态库和找到动态库的位置
strace命令行选项说明如表1。常用的选项为-t, -T, -e, -o等。
表1 命令strace的命令行选项说明
选项
说明
-c
统计每个系统调用执行的时间、次数和出错的次数等。
-d
输出一些strace自身的调试信息到标准输出。
-f
跟踪当前进程由系统调用fork产生的子进程。
-ff
如果使用选项-o filename,则将跟踪结果输出到相应的filename.pid中,pid是各进程的进程号。
-F
尝试跟踪vfork调用.在-f时,vfork不被跟踪。
-h
输出简要的帮助信息。
-i
在系统调用的时候打印指令指针。
-q
禁止输出关于粘附和脱离的信息,发生在输出重定向到文件且直接而不是粘附运行命令时。
-r
依赖于每个系统调用的入口打印相对时间戳。
-t
在输出中的每一行前加上时间信息。
-tt
在输出中的每一行前加上时间信息,包括毫秒。
-ttt
毫秒级输出,以秒表示时间。
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮