linux下的coredump

基本概念

coredump:当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成“核心转储”)。我们可以认为 core dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。
上面说当程序运行过程中异常终止或崩溃时会发生 core dump,但还没说到什么具体的情景程序会发生异常终止或崩溃,例如我们使用 kill -9 命令杀死一个进程会发生 core dump 吗?实验证明是不能的,那么什么情况会产生呢?

  • core的产生

Linux 中信号是一种异步事件处理的机制,每种信号对应有其默认的操作,你可以在 这里 查看 Linux 系统提供的信号以及默认处理。默认操作主要包括忽略该信号(Ingore)、暂停进程(Stop)、终止进程(Terminate)、终止并发生core dump(core)等。如果我们信号均是采用默认操作,那么,以下列出几种信号,它们在发生时会产生 core dump:

SignalActionComment
SIGQUITCoreQuit from keyboard
SIGILLCoreIllegal Instruction
SIGABRTCoreAbort signal from abort
SIGSEGVCoreInvalid memory reference
SIGTRAPCoreTrace/breakpoint trap

当然不仅限于上面的几种信号。这就是为什么我们使用 Ctrl+z 来挂起一个进程或者 Ctrl+C 结束一个进程均不会产生 core dump,因为前者会向进程发出 SIGTSTP 信号,该信号的默认操作为暂停进程(Stop Process);后者会向进程发出SIGINT 信号,该信号默认操作为终止进程(Terminate Process)。同样上面提到的 kill -9 命令会发出 SIGKILL 命令,该命令默认为终止进程。而如果我们使用 Ctrl+\ 来终止一个进程,会向进程发出 SIGQUIT 信号,默认是会产生 core dump 的。还有其它情景会产生 core dump, 如:程序调用 abort() 函数、访存错误、非法指令等等。

终端下比较 Ctrl+CCtrl+\

[niuxiaoke@niuxiaoke]# sleep 10
^C       //使用Ctrl+C终止该程序,不会产生core dump
[niuxiaoke@niuxiaoke]# sleep 10
^\Quit (core dumped) //使用Ctrl+\终止程序,会产生core dump文件。
[niuxiaoke@niuxiaoke]# ll  core.3681  //多了一个core文件
-rw-------. 1 niuxiaoke niuxiaoke 159744 Dec 25 19:17 core.3681

小程序产生 core dump

#include<stdio.h>
int mai()
{
  int *ptr = null;
  ptr = 10;
  return 0;
}
#编译执行
niuxiaoke@niuxiaoke:~$ ./a.out
Segmentation fault (core dumped)
niuxiaoke@niuxiaoke:~$ ls      //多出下面一个 core 文件
-rw-------  1 guohailin guohailin 200704 1022 11:35 a.out.core.22070    

开启coredump

可以使用命令ulimit开启,也可以在程序中通过setrlimit系统调用开启。

  • 打开coredump功能
    ·在终端中输入 ulimit -c ,输出结果为0,说明默认是关闭的core dump,也就是当程序异常终止时,也不会生成core dump文件。
    ·我们可以使用命令 ulimit -c unlimited来开启core dump功能,并且不限制core dump文件的大小,(这里改为了无穷大,也可以设置为你需要的大小,单位是blocks(KB))。
    ·上面的命令只对当前的终端环境有效,如果想永久生效,可以修改文件/etc/security/limits.conf文件。
    ·修改 core 文件保存的路径
    默认生成的 core 文件保存在可执行文件所在的目录下,文件名就为 core。
    通过修改 /proc/sys/kernel/core_uses_pid 文件可以让生成 core 文件名是否自动加上 pid 号。
    例如 echo 1 > /proc/sys/kernel/core_uses_pid ,生成的 core 文件名将会变成 core.pid,其中 pid 表示该进程的 PID。
    还可以通过修改 /proc/sys/kernel/core_pattern 来控制生成 core 文件保存的位置以及文件名格式。

    使用gdb调试core文件

  • 首先,使用 gcc 编译源文件,加上 -g 以增加调试信息;

  • 按照上面打开 core dump 以使程序异常终止时能生成 core 文件;

  • 运行程序,当core dump 之后,使用命令 gdb program core 来查看 core 文件,其中 program 为可执行程序名,core 为生成的 core 文件名。

    下面用一个简单的例子来说明:

#include<stdio.h>

int func(int *p)
{
    int y = p;   
    return y;
}
int main()
{
  int *ptr = null;
  return func(ptr);

}

编译加上调试信息, 运行之后core dump, 使用 gdb 查看 core 文件.

niuxiaoke@niuxiaoke:~$ gcc core_demo.c -o core_demo -g
niuxiaoke@niuxiaoke:~$ ./core_demo 
Segmentation fault (core dumped)

niuxiaoke@niuxiaoke:~$ gdb core_demo core_demo.core.24816

Core was generated by './core_demo'.
Program terminated with signal 11, Segmentation fault.
#0  0x080483cd in func (p=0x0) at core_demo.c:5
5       int y = *p;
(gdb)  where
#0  0x080483cd in func (p=0x0) at core_demo.c:5
#1  0x080483ef in main () at core_demo.c:12
(gdb) info frame
Stack level 0, frame at 0xffd590a4:
 eip = 0x80483cd in func (core_demo.c:5); saved eip 0x80483ef
 called by frame at 0xffd590c0
 source language c.
 Arglist at 0xffd5909c, args: p=0x0
 Locals at 0xffd5909c, Previous frame's sp is 0xffd590a4
 Saved registers:
  ebp at 0xffd5909c, eip at 0xffd590a0
(gdb) 

从上面可以看出,我们可以还原 core_demo 执行时的场景,并使用 where 可以查看当前程序调用函数栈帧, 还可以使用 gdb 中的命令查看寄存器,变量等信息.

  • 如何判断一个文件时core dump 文件
    答:在类unix系统下,coredump文件本身主要的格式也是ELF格式,因此,我们可以通过readelf命令进行判断
[root@localhost Signal]# readelf core.4466 -h
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
> Type:                              CORE (Core file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         14
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0
[root@localhost Signal]# file core.4466
core.4466: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from './test'

附一些gdb常用调试命令:

1,l(list) ,显示源代码,并且可以看到对应的行号;
b(break)x, x是行号,表示在对应的行号位置设置断点;
p(print)x, x是变量名,表示打印变量x的值
r(run), 表示继续执行到断点的位置
n(next),表示执行下一步
c(continue),表示继续执行
q(quit),表示退出gdb

启动gdb,注意该程序编译需要-g选项进行。

2, 一些常用signal的含义
SIGABRT:调用abort函数时产生此信号。进程异常终止。
SIGBUS:指示一个实现定义的硬件故障。
SIGEMT:指示一个实现定义的硬件故障。EMT这一名字来自PDP-11的emulator trap 指令。
SIGFPE:此信号表示一个算术运算异常,例如除以0,浮点溢出等。
SIGILL:此信号指示进程已执行一条非法硬件指令。4.3BSD由abort函数产生此信号。SIGABRT现在被用于此。
SIGIOT:这指示一个实现定义的硬件故障。IOT这个名字来自于PDP-11对于输入/输出TRAP(input/outputTRAP)指令的缩写。系统V的早期版本,由abort函数产生此信号。SIGABRT现在被用于此。
SIGQUIT:当用户在终端上按退出键(一般采用Ctrl-/)时,产生此信号,并送至前台进
程组中的所有进程。此信号不仅终止前台进程组(如SIGINT所做的那样),同时产生一个core文件。
SIGSEGV:指示进程进行了一次无效的存储访问。名字SEGV表示“段违例(segmentationviolation)”。
SIGSYS:指示一个无效的系统调用。由于某种未知原因,进程执行了一条系统调用指令,但其指示系统调用类型的参数却是无效的。
SIGTRAP:指示一个实现定义的硬件故障。此信号名来自于PDP-11的TRAP指令。
SIGXCPUSVR4和4.3+BSD支持资源限制的概念。如果进程超过了其软C P U时间限制,则产生此信号。
SIGXFSZ:如果进程超过了其软文件长度限制,则SVR4和4.3+BSD产生此信号。

3, Core_pattern的格式 可以在core_pattern模板中使用变量还很多,见下面的列表:
%% 单个%字符
%p 所dump进程的进程ID
%u 所dump进程的实际用户ID
%g 所dump进程的实际组ID
%s 导致本次core dump的信号
%tcore dump的时间 (由1970年1月1日计起的秒数)
%h 主机名 %e 程序文件名

  • 产生core dump的一些条件
    1,内存访问越界
    a) 由于使用错误的下标,导致数组访问越界。
    b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符。
    c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。
    2,多线程程序使用了线程不安全的函数。
    3,多线程读写的数据未加锁保护。
    对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成coredump
    4,非法指针
    a) 使用空指针
    b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。
    5,堆栈溢出
    不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。

参考:
http://www.cnblogs.com/hazir/p/linxu_core_dump.htm
http://www.cnblogs.com/niocai/archive/2012/04/01/2428128.html
http://baidutech.blog.51cto.com/4114344/904419/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值