基本概念
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:
Signal | Action | Comment |
---|---|---|
SIGQUIT | Core | Quit from keyboard |
SIGILL | Core | Illegal Instruction |
SIGABRT | Core | Abort signal from abort |
SIGSEGV | Core | Invalid memory reference |
SIGTRAP | Core | Trace/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+C
和 Ctrl+\
:
[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 10月 22 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/