linux kexeccentos kdumpp怎么从新的内核启动

This site in other countries/regions:温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
阅读(179)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'有熟悉Kdump的?请教个问题,谢谢。。。',
blogAbstract:'有熟悉Kdump的?请教个问题,谢谢。。。[quote]怎样配置kdump1.确认kexec-tools已经安装:#rpm -q kexec-tools2.配置/etc/kdump.conf文件,指定vmcore将被转储的路径。可以通过scp拷贝到另一个服务器,也可以是裸设备,或者本地的文件系统。3.修改一些启动参数,为捕获很保留一块内存。对于i386和x86_64架构,编辑/etc/grub.conf,在内核行的末尾添加 crashkernel=128@16M。下面是一个带有kdump选项的/etc/grub.conf文件:# grub.conf generated by anaconda## Note that you do not have to rerun grub after making changes to this file# NOTICE:You do not have a',
blogTag:'',
blogUrl:'blog/static/7',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:7,
permalink:'blog/static/7',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}4801人阅读
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。
  使用调试器来一步步地跟踪代码,查看变量和计算机寄存器的值。在内核中使用交互式调试器是一个很复杂的问题。内核在它自己的地址空间中运行。许多用户空间下的调试器所提供的常用功能很难用于内核之中,比如断点和单步调试等。
(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;
函数faulty_write产生oops信息列出如下(注意 EIP 行和 stack 跟踪记录中已经解码的符号):
Unable to handle kernel NULL pointer dereference at virtual address \
printing eip: c48370c3 *pde =
Oops: 0002 CPU:0 EIP: 0010:[faulty:faulty_write+3/576]
eax: ffffffea ebx: c2c55ae0 ecx: c48370c0 edx: c2c55b00 esi:
ebp: c2337f8c esp: c2337f8c ds: 0018 es: 0018 ss: 0018 Processcat
(pid: 23413,stackpage=c2337000) Stack: 356e6 c2c55ae0 0b00 c2336000 \
bffffbd4 00000 bffffbd4 c010b860
d038 bffffbd4 2b \
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)
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);
return count;
return ret;
函数faulty_read产生oops信息列出如下:
EIP: 0010:[&&]
Unable to handle kernel paging request at virtual address ffffffff printing eip: ffffffff Oops: 0000[#5] SMP
CPU: 0 EIP: 0060:[] Not tainted EFLAGS: (2.6.6) EIP is at 0xffffffff eax: 0000000c ebx: ffffffff ecx:
edx: bfffda7c
esi: cf434f00 edi: ffffffff ebp:
esp: c27fff78 ds: 007b es: 007b ss: 0068 Processhead
(pid: 2331,threadinfo=c27fe000
task=c3226150) Stack: ffffffff bfffda70
cf434f20 00286 cf434f00 fffffff7 bfffda70 c27fe000 c0150612 cf434f00 bfffda70
cf434f20 00 c000003 bfffda70
02000 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在崩溃时触发启动新内核,存储旧内存映像以便于调试,让系统在新内核上运行 ,从而避免了死机,增强了系统的稳定性。
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,表示告诉系统内核保留从物理地址0xMB)开始的64MB大小给转储捕捉内核使用。通常x86和x86_64平台设置&crashkernel=64M@16M&,ppc64平台设置&crashkernel=128M@32M&。
5)装载转储捕捉内核
在启动进入系统内核后,需要装载转储捕捉内核。根据处理器构架和映射文件的类型(可否重定位),可以选择装载不压缩的vmlinux或压缩的bzImage/vmlinuz内核映像。选择方法说明如下:
对于i386和x86_64平台:
如果内核不是可重定位的,使用vmlinux。如果内核是可重定位的,使用bzImage/vmlinuz。
对于ppc64平台:
使用vmlinux。
对于ia64平台:
使用vmlinux或vmlinuz.gz。
如果用户使用不压缩的vmlinux映像,那么使用下面的命令装载转储捕捉内核:
kexec -p &dump-capture-kernel-vmlinux-image& \
--initrd=&initrd-for-dump-capture-kernel& --args-linux \
--append=&root=&root-dev& &arch-specific-options&&
如果用户使用压缩的bzImage/vmlinuz映像,那么使用下面的命令装载转储捕捉内核:
kexec -p &dump-capture-kernel-bzImage&\
--initrd=&initrd-for-dump-capture-kernel& \
--append=&root=&root-dev& &arch-specific-options&&
注意:参数--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输出的根设备名的格式指定&root-dev&。启动参数&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 &dump-file&
用户还可以将转储内存作为设备/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&魔术组合键&是一组按键,由键盘上的&Alt+SysRq+[CommandKey]&三个键组成,其中CommandKey为可选的按键。SysRq魔术组合键根据组合键的不同,可提供控制内核或打印内核信息的功能。SysRq魔术组合键的功能说明如表1所示。
表1 SysRq组合键的功能说明
在没有同步或卸载硬盘的情况下立即启动。
为了获取崩溃转储执行kexe重启动。
显示被持的所有锁。
发送信号SIGTERM给所有进程,除了init外。
将调用oom_kill杀死内存热进程。
在平台ppc和sh上被kgdb使用。
显示帮助信息。
发送信号SIGKILL给所有的进程,除了init外。
安全访问密钥(Secure Access Key,SAK)杀死在当前虚拟终端上的所有程序。
转储当前的内存信息到控制台。
用于设置实时任务为可调整nice的。
将关闭系统(如果配置为支持)。
打印当前寄存器和标识到控制台。
将转储所有正运行定时器的列表。
关闭键盘Raw模式并设置为XLATE模式。
尝试同步所有挂接的文件系统。
将转储当前的任务列表和它们的信息到控制台。
尝试以仅读的方式重挂接所有已挂接的文件系统。
转储Voyager SMP处理器信息到控制台。
转储的所有非可中断(已阻塞)状态的任务。
在平台ppc/powerpc上被xmon(X监视器)接口使用。
设备控制台日志级别,控制将打印到控制台的内核信息。例如: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:
pid father
Oct 29 17:51:43 njllinux kernel: init
S ffffffff812b76a0
Oct 29 17:51:43 njllinux kernel: ffff8
Oct 29 17:51:43 njllinux kernel: fa97978 ffffffff ffffffff ffff8
Oct 29 17:51:43 njllinux kernel: ffffffff813cc5b0 ffff8 2a50 ffff8
Oct 29 17:51:43 njllinux kernel: Call Trace:
Oct 29 17:51:43 njllinux kernel:
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: [&ffffffff&] tracesys+0xd0/0xd5
Oct 29 17:51:43 njllinux kernel:
Oct 29 17:51:43 njllinux kernel: lighttpd
S ffffffff812b76a0
Oct 29 17:51:43 njllinux kernel: ffffb18
Oct 29 17:51:43 njllinux kernel: ffff8 ffffffff ffffffff ffffa0
Oct 29 17:51:43 njllinux kernel: ffffffff813cc5b0 fffff0 d49ac8 fffff0
Oct 29 17:51:43 njllinux kernel: Call Trace:
Oct 29 17:51:43 njllinux kernel: [&ffffffff&] ? __mod_timer+0xbb/0xcd
Oct 29 17:51:43 njllinux kernel: [&ffffffff8129b2ee&] schedule_timeout+0x8d/0xb4
Oct 29 17:51:43 njllinux kernel: [&ffffffff&] ? process_timeout+0x0/0xb
Oct 29 17:51:43 njllinux kernel: [&ffffffff&] ? schedule_timeout+0x88/0xb4
Oct 29 17:51:43 njllinux kernel: [&ffffffff810b9498&] do_sys_poll+0x2a8/0x370
命令strace 显示程序调用的所有系统调用。使用 strace 工具,用户可以清楚地看到这些调用过程及其使用的参数,了解它们与操作系统之间的底层交互。当系统调用失败时,错误的符号值(如 ENOMEM)和对应的字符串(如Out of memory)都能被显示出来。
starce 的另一个用处是解决和动态库相关的问题。当对一个可执行文件运行ldd时,它会告诉你程序使用的动态库和找到动态库的位置
strace命令行选项说明如表1。常用的选项为-t, -T, -e, -o等。
表1 命令strace的命令行选项说明
统计每个系统调用执行的时间、次数和出错的次数等。
输出一些strace自身的调试信息到标准输出。
跟踪当前进程由系统调用fork产生的子进程。
如果使用选项-o filename,则将跟踪结果输出到相应的filename.pid中,pid是各进程的进程号。
尝试跟踪vfork调用.在-f时,vfork不被跟踪。
输出简要的帮助信息。
在系统调用的时候打印指令指针。
禁止输出关于粘附和脱离的信息,发生在输出重定向到文件且直接而不是粘附运行命令时。
依赖于每个系统调用的入口打印相对时间戳。
在输出中的每一行前加上时间信息。
在输出中的每一行前加上时间信息,包括毫秒。
毫秒级输出,以秒表示时间。
显示系统调用所花费的时间。
输出所有的系统调用的信息。一些关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出。
输出strace的版本信息。
以十六进制形式输出非ASCII标准字符串。
所有字符串以十六进制形式输出。
以特定的列数对齐返回值,缺省值为40。
指定一个表达式,用来控制如何跟踪.格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一。value是用来限定的符号或数字。默认的qualifier是 trace。感叹号是否定符号。
等价于 -e trace=open,表示只跟踪open调用。而-etrace!=open表示跟踪除了open以外的其他调用。
-e trace=set
只跟踪指定的系统调用。例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用。默认的为set=all。
-e trace=file
只跟踪文件名作为参数的系统调用,一般为文件操作。
-e trace=process
只跟踪有关进程控制的系统调用。
-e trace=network
只跟踪与网络有关的所有系统调用。
-e strace=signal
跟踪所有与系统信号有关的系统调用。
-e trace=ipc
跟踪所有与进程间通信有关的系统调用。
-o filename
将strace的输出写入文件filename。
跟踪指定的进程pid。
-s strsize
指定最大字符串打印长度,默认值为32。
-u username
以username的UID和GID执行命令。
例如:命令strace pwd的输出部分列出如下:
execve(&/bin/pwd&, [&pwd&], [/* 39 vars */]) = 0
uname({sys=&Linux&, node=&sammy&, ...}) = 0
= 0x804c000
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4001...
fstat64(3, {st_mode=S_IFREG|0644, st_size=115031, ...}) = 0
old_mmap(NULL, 115031, PROT_READ, MAP_PRIVATE, 3, 0) = 0x
open(&/lib/tls/libc.so.6&, O_RDONLY)
read(3, &\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360U\1&..., 1024) = 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=1547996, ...}) = 0
Linux内核用函数printk打印调试信息,该函数的用法与C库打印函数printf格式类似,但在内核使用。用户可在内核代码中的某位置加入函数printk,直接把所关心的信息打打印到屏幕上或日志文件中。
函数printk根据日志级别(loglevel)对调试信息进行分类。日志级别用宏定义,展开为一个字符串,在编译时由预处理器将它和消息文本拼接成一个字符串,因此函数printk中的日志级别和格式字符串间不能有逗号。
下面两个 printk 的例子,一个是调试信息,一个是临界信息:
printk(KERN_DEBUG &Here I am: %s:%i\n&, _ _FILE_ _, _ _LINE_ _);
printk(KERN_CRIT &I' giving up on %p\n&, ptr);
样例:在用户空间或内核中开启及关闭打印调试消息 用户还可以在内核或用户空间应用程序定义统一的函数打印调试信息,可在Makefile文件中打开或关闭调试函数。定义方法列出如下:
/*debug_on_off.h*/
#undef PDEBUG
/* undef it, just in case */
#ifdef SCULL_DEBUG
#ifdef _ _KERNEL_ _
/* This one if debugging is on, and kernel space */
#define PDEBUG(fmt,args...) printk(KERN_DEBUG &scull: & fmt, ## args)
/* This one for user space */
#define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
#define PDEBUG(fmt, args...) /* not debugging: nothing */
在文件Makefile加上下面几行:
# Comment/uncomment the following line to disable/enable debugging
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # &-O&
DEBFLAGS = -O2
CFLAGS += $(DEBFLAGS)
更改makefile中的DEBUG值,需要调试信息时,DEBUG = y,不需要时,DEBUG赋其它值。再用make编译即可。
kprobe(内核探测,kernel probe)是一个动态地收集调试和性能信息的工具,如:收集寄存器和全局数据结构等调试信息,无需对Linux内核频繁编译和启动。用户可以在任何内核代码地址进行陷阱,指定调试断点触发时的处理例程。工作机制是:用户指定一个探测点,并把用户定义的处理函数关联到该探测点,当内核执行到该探测点时,相应的关联函数被执行,然后继续执行正常的代码路径。
kprobe允许用户编写内核模块添加调试信息到内核。当在远程机器上调试有bug的程序而日志/var/log/messages不能看出错误时,kprobe显得非常有用。用户可以编译一个内核模块,并将内核模块插入到调试的内核中,就可以输出所需要的调试信息了。
内核探测分为kprobe, jprobe和kretprobe(也称return probe,返回探测)三种。kprobe可插入内核中任何指令处;jprobe插入内核函数入口,方便于访问函数的参数;return probe用于探测指定函数的返回值。
内核模块的初始化函数init安装(或注册)了多个探测函数,内核模块的退出函数exit将注销它们。注册函数(如:register_kprobe())指定了探测器插入的地方、探测点触发的处理例程。
(1)配置支持kprobe的内核
配置内核时确信在.config文件中设置了CONFIG_KPROBES、CONFIG_MODULES、CONFIG_MODULE_UNLOAD、CONFIG_KALLSYMS_ALL和CONFIG_DEBUG_INFO。
配置了CONFIG_KALLSYMS_ALL,kprobe可用函数kallsyms_lookup_name从地址解析代码。配置了CONFIG_DEBUG_INFO后,可以用命令&objdump -d -l vmlinux&查看源到对象的代码映射。
调试文件系统debugfs含有kprobe的调试接口,可以查看注册的kprobe列表,还可以关闭/打开kprobe。
查看系统注册probe的方法列出如下:
#cat /debug/kprobes/list
vfs_read+0x0
do_fork+0x0
tcp_v4_rcv+0x0
第一列表示探测点插入的内核地址,第二列表示内核探测的类型,k表示kprobe,r表示kretprobe,j表示jprobe,第三列指定探测点的&符号+偏移&。如果被探测的函数属于一个模块,模块名也被指定。
打开和关闭kprobe的方法列出如下:
#echo ‘1’ /debug/kprobes/enabled
#echo ‘0’ /debug/kprobes/enabled
(2)kprobe样例
Linux内核源代码在目录samples/kpobges下提供了各种kprobe类型的探测处理例程编写样例,分别对应文件kprobe_example.c、jprobe_example.c和kretprobe_example.c,用户稍加修改就可以变成自己的内核探测模块。下面仅说明kprobe类型的探测例程。
样例kprobe_example是kprobe类型的探测例程内核模块,显示了在函数do_fork被调用时如何使用kprobe转储栈和选择的寄存器。当内核函数do_fork被调用创建一个新进程时,在控制台和/var/log/messages中将显示函数printk打印的跟踪数据。样例kprobe_example列出如下(在samples/kprobe_example.c中):
#include &linux/kernel.h&
#include &linux/module.h&
#include &linux/kprobes.h&
/* 对于每个探测,用户需要分配一个kprobe对象*/
static struct kprobe kp = {
.symbol_name = &do_fork&,
/* 在被探测指令执行前,将调用预处理例程 pre_handler,用户需要定义该例程的操作*/
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
#ifdef CONFIG_X86
printk(KERN_INFO &pre_handler: p-&addr = 0x%p, ip = %lx,&
& flags = 0x%lx\n&,
p-&addr, regs-&ip, regs-&flags);
/*打印地址、指令和标识*/
#ifdef CONFIG_PPC
printk(KERN_INFO &pre_handler: p-&addr = 0x%p, nip = 0x%lx,&
& msr = 0x%lx\n&,
p-&addr, regs-&nip, regs-&msr);
/* 在这里可以调用内核接口函数dump_stack打印出栈的内容*/
/* 在被探测指令执行后,kprobe调用后处理例程post_handler */
static void handler_post(struct kprobe *p, struct pt_regs *regs,
unsigned long flags)
#ifdef CONFIG_X86
printk(KERN_INFO &post_handler: p-&addr = 0x%p, flags = 0x%lx\n&,
p-&addr, regs-&flags);
#ifdef CONFIG_PPC
printk(KERN_INFO &post_handler: p-&addr = 0x%p, msr = 0x%lx\n&,
p-&addr, regs-&msr);
/*在pre-handler或post-handler中的任何指令或者kprobe单步执行的被探测指令产生了例外时,会调用fault_handler*/
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
printk(KERN_INFO &fault_handler: p-&addr = 0x%p, trap #%dn&,
p-&addr, trapnr);
/* 不处理错误时应该返回*/
/*初始化内核模块*/
static int __init kprobe_init(void)
kp.pre_handler = handler_pre;
kp.post_handler = handler_post;
kp.fault_handler = handler_fault;
ret = register_kprobe(&kp);
/*注册kprobe*/
if (ret & 0) {
printk(KERN_INFO &register_kprobe failed, returned %d\n&, ret);
return ret;
printk(KERN_INFO &Planted kprobe at %p\n&, kp.addr);
static void __exit kprobe_exit(void)
unregister_kprobe(&kp);
printk(KERN_INFO &kprobe at %p unregistered\n&, kp.addr);
module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE(&GPL&);
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:49520次
排名:千里之外
原创:11篇
转载:44篇
(1)(2)(1)(1)(3)(1)(2)(7)(2)(1)(6)(12)(1)(1)(14)}

我要回帖

更多关于 starting kdump 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信