core文件的简单介绍 |
---|
|
2007-04-13 12:06:30 Linux联盟收集整理 |
|
//--------------------------------------------------------------- 1. core文件的简单介绍 //---------------------------------------------------------------
在一个程序崩溃时,它一般会在指定目录下生成一个core文件。core文件仅仅是一个内存映象(同时加上调试信息),主要是用来调试的。
//--------------------------------------------------------------- 2. 开启或关闭core文件的生成 //---------------------------------------------------------------
用以下命令来阻止系统生成core文件: ulimit -c 0 下面的命令可以检查生成core文件的选项是否打开: ulimit -a 该命令将显示所有的用户定制,其中选项-a代表“all”。
也可以修改系统文件来调整core选项 在/etc/profile通常会有这样一句话来禁止产生core文件,通常这种设置是合理的: # No core files by default ulimit -S -c 0 > /dev/null 2>&1 但是在开发过程中有时为了调试问题,还是需要在特定的用户环境下打开core文件产生的设置 在用户的~/.bash_profile里加上ulimit -c unlimited来让特定的用户可以产生core文件 如果ulimit -c 0 则也是禁止产生core文件,而ulimit -c 1024则限制产生的core文件的大小不能超过1024kb
//--------------------------------------------------------------- 3. 设置Core Dump的核心转储文件目录和命名规则 //---------------------------------------------------------------
/proc/sys/kernel/core_uses_pid可以控制产生的core文件的文件名中是否添加pid作为扩展,如果添加则文件内容为1,否则为0
echo "1"> /proc/sys/kernel/core_uses_pid
proc/sys/kernel/core_pattern可以设置格式化的core文件保存位置或文件名,比如原来文件内容是core-%e 可以这样修改: echo "/corefile/core-%e-%p-%t" > core_pattern
echo"/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern,可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
将会控制所产生的core文件会存放到/corefile目录下,产生的文件名为core-命令名-pid-时间戳 以下是参数列表: %p - insert pid into filename 添加pid %u - insert current uid into filename 添加当前uid %g - insert current gid into filename 添加当前gid %s - insert signal that caused the coredump into the filename 添加导致产生core的信号 %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间 %h - insert hostname where the coredump happened into filename 添加主机名 %e - insert coredumping executable name into filename 添加命令名
//--------------------------------------------------------------- 4. 使用core文件 //---------------------------------------------------------------
在core文件所在目录下键入: gdb -c core 它会启动GNU的调试器,来调试core文件,并且会显示生成此core文件的程序名,中止此程序的信号等等 如果你已经知道是由什么程序生成此core文件的,比如MyServer崩溃了生成core.12345,那么用此指令调试: gdb -c core MyServer 以下怎么办就该去学习gdb的使用了
//--------------------------------------------------------------- 5. 一个小方法来测试产生core文件 //---------------------------------------------------------------
直接输入指令: kill -s SIGSEGV $$
|
以前写的一个文档,有同学反馈不能用,恰好手头有一个堆栈被破坏的core文件要查看,跑了一遍重新更新了一下原来的文档。
-----------------------------------------------------
调试core文件的时候,偶尔会遇到调用堆栈被破坏的情况,一大串问号令人头痛。类似下面:

echou大师介绍了一种手动恢复调用堆栈的方法,cesc研究了一下,跟大家share下。
源文档见http://devpit.org/wiki/x86ManualBacktrace
首先回顾一些背景知识:
%esp 是堆栈指针寄存器,它指向当前堆栈储存区域的顶部.
�p 是基址寄存器,它指向当前堆栈储存区域的底部.
%eip 是指令指针,是下一条要执行的指令地址.
当发生函数调用时,程序如下操作:首先把参数压入堆栈;然后保存指令寄存器(EIP)中的内容做为返回地址(RET);第三个放入堆栈的是基址寄存器(EBP);然后把当前的栈指针(ESP)拷贝到EBP,做为新的基地址;最后为本地变量留出一定空间,把ESP减去适当的数值。如下所示:
Low addresses
-----------------------------------
0(%esp) | 栈顶
---------|-------------------------
-n(�p) | 被调函数的栈空间,局部变量在此分配,这段是可变的
0(�p) |上层函数调用的栈基地址
4(�p) |返回地址
n(�p) |一系列函数参数
-----------------------------------
High addresses
思路就是:通过查看寄存器内容,一步步得到每一层调用的返回地址,最后恢复函数调用链。
1. 首先,查看寄存器信息,找到ebp ,即当前函数调用堆栈的基址

这就是当前调用栈:
StackFrame Pointer | Instruction Pointer
------------------------------------------
0xb50c41a8(ebp) | 0xb7b15197(eip)
2. 然后,查看ebp中的地址内容,获取上层调用的ebp,eip

0xb50c41a8 这地址存放的就是上层调用栈的基址,0xb50c41a8 存放上层调用的EIP
看一下内存地址 0xb50c41a8附近的内容,在里面找上层�p 和 �p+4的内容,下面已经用方框标出

注意,这里的地址内容是小端字节序,整理后是这样的
0xb50c41a8: 0xb50c41b8 0xb79ec77f ……
下面这就是我们恢复的coredump时候的几层堆栈信息:
StackFrame Pointer | Instruction Pointer
-----------------------------------------------
0xb50c41a8 0xb7b15197
0xb50c41a8: 0xb50c41b8 0xb79ec77f
0xb50c41b8: 0xb50c4218 0xb79e0822
0xb50c4218: 0xb50c4258 0xb79e0251
0xb50c4258: 0xb50c4278 0xb79df41c
0xb50c4278: 0xb50c42a8 0xb7e2cd86
0xb50c42a8: 0xb50c5468 0x0805be44 ……
这样找下去,可以一直找到最上一层调用,到达栈底;不过,一般不需要找到头
3. 有了每一层调用发生时的返回地址(EIP), 直接用指令格式查看内存内容就行了:

0xb79ec77f <_ZN7log4cpp3NDC3getEv+31>: add $0x4,%esp
前半段就是当前调用函数symbol name。走到这里,调用堆栈已经恢复了。
二
在Unix系统下,如果send、recv、write在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。此种情况应用就很难查出处理进程为什么退出。
SIGPIPE信号:
对 一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法 时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全 关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出。如果对SIGPIPE进行忽略处理,二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE.
处理方法:
在初始化时调用signal(SIGPIPE,SIG_IGN)忽略该信号(只需一次),SIGPIPE交给了系统处理。此时send、recv或write函数将返回-1,errno为EPIPE,可视情况关闭socket或其他处理
SIGPIPE被忽略的情况下,如果服务器采用了fork的话,要收集垃圾进程,防止僵尸进程的产生,可以这样处理: signal(SIGCHLD,SIG_IGN); 交给系统init去回收。这样子进程就不会产生僵尸进程了。
Coredump生成过程:
系统关键/核心进程,产生严重的无法恢复的错误,为了避免系统相关资源受到更大损害,操作系统都会强行停止运行,并将当前内存中的各种结构、核心进程出错位置及其代码状态,保存下来,以便以后分析。最常见的原因是指令走飞,或者缓冲区溢出,或者内存访问越界。
进程退出,未发现core的原因有一下几种:
1、core文件的生成开关被关闭
a) 查看core文件的生成开关:
boss@Tencent:~> ulimit -c
0
b) 打开core文件的生成开关:
ulimit -c unlimited
2、core文件的生成开关已被打开,当前路径下找不到core文件
a) /proc/sys/kernel/core_pattern文件可以控制core文件保存位置和文件名格式
echo "core" >/proc/sys/kernel/core_pattern 当前目录生成core
b) 系统生成的core文件以pid作为扩展名称
echo "1" > /proc/sys/kernel/core_uses_pid
3、系统中存在某个逻辑上return错误地使用了exit、_exit、abort
exit、_exit和abort它们会直接退出进程,而不是返回堆栈。此情况系统是系统被正常的退出了,而不是出现异常。
4、系统中的异常信号没有进行处理,操作系统结束了此进程
操作系统对信号有以下几种默认操作,对于A类信号如果进程不做处理,进程会被操作系统终止,也不会产生core。
信号类型 | A 缺省的动作是终止进程 B 缺省的动作是忽略此信号 C 缺省的动作是终止进程并进行内核映像转储(dump core) D 缺省的动作是停止进程 E 信号不能被捕获 F 信号不能被忽略 |
A类信号 | SIGHUP 1 A 终端挂起或者控制进程终止 SIGINT 2 A 键盘中断(如break键被按下) SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道 SIGALRM 14 A 由alarm(2)发出的信号 SIGTERM 15 A 终止信号 SIGUSR1 30,10,16 A 用户自定义信号1 SIGUSR2 31,12,17 A 用户自定义信号2 SIGPROF 27,27,29 A Profiling定时器到 SIGVTALRM 26,26,28 A 实际时间报警时钟信号(4.2 BSD) SIGSTKFLT -,16,- A 协处理器堆栈错误 SIGIO 23,29,22 A 某I/O操作现在可以进行了(4.2 BSD) SIGCLD -,-,18 A 与SIGCHLD同义 SIGPWR 29,30,19 A 电源故障(System V) SIGLOST -,-,- A 文件锁丢失 |
转自:http://blog.sina.com.cn/s/blog_45497dfa0100mddb.html