Core Dump详解

来源:互联网 发布:征途 巨人网络 编辑:程序博客网 时间:2024/06/07 18:50

什么是Core Dump

在UNIX系统中,常将“主内存”(main memory) 称为核心(core),而核心映像(core image) 就是 “进程”(process)执行当时的内存内容。当进程发生错误或收到“信号”(signal) 而终止执行时,系统会将核心映像写入一个文件,以作为调试之用,这就是所谓的核心转储(core dump)。

通常情况下core dump包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息等。可以理解为把程序工作的当前状态存储成一个文件。许多程序和操作系统出错时会自动生成一个core文件。

Core Dump功能启用

Core Dump可以通过命令行启用;也可以通过设置配置文件使之全局有效。

命令:
ulimit -c
查看当前core文件允许大小,通过命令: 如果输出为 0 ,则代表没有打开。如果为unlimited则已经打开了,大小不限,就没必要再做打开;
ulimit -c unlimited
打开Core Dump功能,core大小不限;
ulimit -c 0
设置core大小为0,即关闭core dump。

通过上面的命令修改后,一般都只是对当前会话起作用,当你下次重新登录后,还是要重新输入上面的命令,所以很麻烦。我们可以把通过修改 /etc/profile文件 来使系统每次自动打开。
步骤如下:
1.首先打开/etc/profile文件
一般都可以在文件中找到这句语句:ulimit -S -c 0 > /dev/null 2>&1.ok,根据上面的例子,我们只要把那个0 改为 unlimited 就ok了。然后保存退出。如果没有这句话,直接加ulimit -c unlimited即可。
2.通过source /etc/profile 使当期设置生效。
3.通过ulimit -c 查看下是否已经打开。

了解Linux环境变量配置的会知道,不仅可以加入到/etc/profile文件中,也可以加到其他的位置,详见http://blog.csdn.net/picway/article/details/70767163。

Core Dump文件存储位置

默认位置与可执行程序在同一目录下,文件名是core.XXX,其中XXX是一个数字。core dump文件名的模式保存在/proc/sys/kernel/core_pattern中,缺省值是core。通过以下命令可以更改core dump文件的位置(如希望生成到/tmp/cores目录下)

echo “/tmp/cores/core” > /proc/sys/kernel/core_pattern

设置Core Dump的核心转储文件目录和命名规则
/proc/sys/kernel/core_uses_pid可以控制产生的core文件的文件名中是否添加pid作为扩展,如果添加则文件内容为1,否则为0
proc/sys/kernel/core_pattern可以设置格式化的core文件保存位置或文件名,比如原来文件内容是core-%e
可以这样修改:
echo “/corefile/core-%e-%p-%t” > core_pattern
将会控制所产生的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 添加命令名

分析core dump

现在大部分类unix操作系统都提供了分析core文件的工具,比如 GNU Binutils Binary File Descriptor library (BFD),GNU Debugger (gdb),mdb等。较流行的是gdb。

在core文件所在目录下键入:

gdb -c core

它会启动GNU的调试器,来调试core文件,并且会显示生成此core文件的程序名,中止此程序的信号等等。
如果你已经知道是由什么程序生成此core文件的,比如MyServer崩溃了生成core.12345,那么用此指令调试:

gdb -c core MyServer

在进入gdb后, 可以用bt命令查看backtrace以检查发生程序运行到哪里, 来定位core dump的文件->行。详细的gdb使用另行学习。

造成程序coredump的原因

1 内存访问越界

a) 由于使用错误的下标,导致数组访问越界
b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符
c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。

2 多线程程序使用了线程不安全的函数。

应该使用下面这些可重入的函数,尤其注意红色标示出来的函数,它们很容易被用错:

asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

3 多线程读写的数据未加锁保护。

对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump

4 非法指针

a) 使用空指针
b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型 的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它 时就很容易因为bus error而core dump.

5 堆栈溢出

不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。

System Dump和Core Dump的区别

许多没有做过UNIX系统级软件开发的人士,可能只听说过Dump,而并不知道系统Dump和Core Dump的区别,甚至混为一谈。

系统Dump(System Dump)

所有开放式操作系统,都存在系统DUMP问题。
产生原因:
由于系统关键/核心进程,产生严重的无法恢复的错误,为了避免系统相关资源受到更大损害,操作系统都会强行停止运行,并将当前内存中的各种结构,核心进程出错位置及其代码状态,保存下来,以便以后分析。最常见的原因是指令走飞,或者缓冲区溢出,或者内存访问越界。走飞就是说代码流有问题,导致执行到某一步指令混乱,跳转到一些不属于它的指令位置去执行一些莫名其妙的东西(没人知道那些地方本来是代码还是数据,而且是不是正确的代码开始位置),或者调用到不属于此进程的内存空间。写过C程序及汇编程序的人士,对这些现象应当是很清楚的。
系统DUMP生成过程的特点:
在生成DUMP过程中,为了避免过多的操作结构,导致问题所在位置正好也在生成DUMP过程所涉及的资源中,造成DUMP不能正常生成,操作系统都用尽量简单的代码来完成,所以避开了一切复杂的管理结构,如文件系统)LVM等等,所以这就是为什么几乎所有开放系统,都要求DUMP设备空间是物理连续的——不用定位一个个数据块,从DUMP设备开头一直写直到完成,这个过程可以只用BIOS级别的操作就可以。这也是为什么在企业级UNIX普遍使用LVM的现状下,DUMP设备只可能是裸设备而不可能是文件系统文件,而且[b]只[/b]用作DUMP的设备,做 LVM镜像是无用的——系统此时根本没有LVM操作,它不会管什么镜像不镜像,就用第一份连续写下去。
所以UNIX系统也不例外,它会将DUMP写到一个裸设或磁带设备。在重启的时候,如果设置的DUMP转存目录(文件系统中的目录)有足够空间,它将会转存成一个文件系统文件,缺省情况下,对于AIX来说是/var/adm/ras/下的vmcore*这样的文件,对于HPUX来说是 /var/adm/crash下的目录及文件。当然,也可以选择将其转存到磁带设备。
会造成系统DUMP的原因主要是:
系统补丁级别不一致或缺少)系统内核扩展有BUG(例如Oracle就会安装系统内核扩展))驱动程序有 BUG(因为设备驱动程序一般是工作在内核级别的),等等。所以一旦经常发生类似的系统DUMP,可以考虑将系统补丁包打到最新并一致化)升级微码)升级设备驱动程序(包括FC多路冗余软件))升级安装了内核扩展的软件的补丁包等等。

进程Core Dump

进程Core Dump产生的技术原因,基本等同于系统DUMP,就是说从程序原理上来说是基本一致的。
但进程是运行在低一级的优先级上(此优先级不同于系统中对进程定义的优先级,而是指CPU代码指令的优先级),被操作系统所控制,所以操作系统可以在一个进程出问题时,不影响其他进程的情况下,中止此进程的运行,并将相关环境保存下来,这就是core dump文件,可供分析。
如果进程是用高级语言编写并编译的,且用户有源程序,那么可以通过在编译时带上诊断用符号表(所有高级语言编译程序都有这种功能),通过系统提供的分析工具,加上core文件,能够分析到哪一个源程序语句造成的问题,进而比较容易地修正问题,当然,要做到这样,除非一开始就带上了符号表进行编译,否则只能重新编译程序,并重新运行程序,重现错误,才能显示出源程序出错位置。
如果用户没有源程序,那么只能分析到汇编指令的级别,难于查找问题所在并作出修正,所以这种情况下就不必多费心了,找到出问题的地方也没有办法。
进程Core Dump的时候,操作系统会将进程异常终止掉并释放其占用的资源,不可能对系统本身的运行造成危害。这是与系统DUMP根本区别的一点,系统DUMP产生时,一定伴随着系统崩溃和停机,进程Core Dump时,只会造成相应的进程被终止,系统本身不可能崩溃。当然如果此进程与其他进程有关联,其他进程也会受到影响,至于后果是什么,就看相关进程对这种异常情况(与自己相关的进程突然终止)的处理机制是什么了,没有一概的定论。

附注:

gdb的查看源码

显示源代码:GDB 可以打印出所调试程序的源代码,当然,在程序编译时一定要加上-g的参数,把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后,GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。还是来看一看查看源代码的GDB命令吧。

list<linenum>

显示程序第linenum行的周围的源程序。

list<function>

显示函数名为function的函数的源程序。

list

显示当前行后面的源程序。

list -

显示当前行前面的源程序。
一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。

setlistsize <count>

设置一次显示源代码的行数。

showlistsize

查看当前listsize的设置。
list命令还有下面的用法:

list<first>, <last>

显示从first行到last行之间的源代码。

list ,<last>

显示从当前行到last行之间的源代码。

list +

往后显示源代码。
一般来说在list后面可以跟以下这些参数:

<linenum>   行号。<+offset>   当前行号的正偏移量。<-offset>   当前行号的负偏移量。<filename:linenum>  哪个文件的哪一行。<function>  函数名。<filename:function>哪个文件中的哪个函数。<*address>  程序运行时的语句在内存中的地址。

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产生此信号。

http://blog.csdn.net/tenfyguo/article/details/8159176/
http://blog.chinaunix.net/uid-26074921-id-2353637.html
http://blog.chinaunix.net/uid-21411227-id-1826911.html

0 0