在Linux下程序不寻常退出时,内核会在当前工作目录下生成一个core文件(是一个内存映像,同时加上调试信息)。使用gdb来查看core文件,可以指示出导致程序出错的代码所在文件和行数。
注: 1. 当然首先编译时要带上gdb信息
1. core文件的生成开关和大小限制
1.1使用ulimit -c
命令可查看core文件的生成开关。
若结果为0,则表示关闭了此功能,不会生成core文件。
1.2 使用ulimit -c filesize
命令,可以限制core文件的大小(filesize的单位为kbyte)。
如果生成的信息超过此大小,将会被裁剪,最终生成一个不完整的core文件或者根本就不生成。
如果生成被裁减的core文件,调试此core文件的时候,gdb也会提示错误。
用以下命令来表示core文件的大小不受限制。
ulimit -c unlimited
用以下命令来阻止系统生成core文件:
ulimit -c 0
下面的命令可以检查生成core文件的选项是否打开:
ulimit -a
该命令将显示所有的用户定制,其中选项-a代表“all”。
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) 4
max memory size (kbytes, -m) unlimited
open files (-n) 2048
pipe size (512 bytes, -p) 8
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 7168
virtual memory (kbytes, -v) unlimited
注:
a) ulimit命令设置后只对一个终端有效,所以另起终端后需要重新设置。
b) 要在整个系统中生效,可以通过如下方法(当然此方法未必管用和linux版本相关):
b.1) 编辑/root/.bash_profile文件,在其中加入:ulimit -S -c unlimited (需要注意的是:不是每个版本的系统都有这个文件(Suse下面就没有),可以手工创建)
b.2) 重启系统或者执行:soruce /root/.bash_profile
c) 关于 ulimit 命令的用法见:http://www.groad.net/bbs/read.php?tid-1471.html
2. core文件的名称和生成路径
core文件生成路径:输入可执行文件运行命令的同一路径下。若系统生成的core文件不带其它任何扩展名称,则全部命名为core。新的core文件生成将覆盖原来的core文件。
2.1. /proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。
文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件同一命名为core。
可通过以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid
2.2 . /proc/sys/kernel/core_pattern或者/etc/sysctl.conf文件,可以控制core文件保存位置和文件名格式。到底哪一个,和系统版本相关,需要实际测试.
2.2.1 修改/proc/sys/kernel/core_pattern, 可通过以下命令修改此文件:
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 添加命令名
注:
a) 目录proc文件系统是一个伪文件系统,以文件系统的方式为访问系统内核数据的操作提供接口。/proc目录的内容为系统启动时自动生成的,某写文件可改,某写
文件不可改。比如可以通过修改proc的文件微调内核参数。使用vi可能无法成功编辑proc/sys/kernel/core_pattern,只能使用echo命令修改或者命令sysctl修改。sysctl命令如下:
sysctl -w "kernel.core_pattern=$core_dir/core_%e_%t" >/dev/null
sysctl -w "kernel.core_uses_pid=0" >/dev/null
b) 即使使用echo命令修改成功,也可能无法成功产生core文件,如所用的centor5.4 装于虚拟机中。proc/sys/kernel/core_pattern内容为:
/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e
按照上述方式修改(echo "/corefile/core-%e-%p-%t" > core_pattern),core文件甚至都不再产生
2.2.2 修改/etc/sysctl.conf( http://www.ms2006.com/archives/151 )
添加需要保存的路径 "kernel.core_pattern = /tmp/corefile/core.%e.%t",需要注意的是该路径必须应用有写的权限,不然core文件是不会生成的。再执行命令"sysctl -p"即可生效。关于core_users_pid默认在sysctl文件里面已经存在,不需要更改,pid还是很重要的信息。
注:
a) 需要注意的是,如果 /tmp/corefile目录原先不存在,那么生成的 core dump 文件就无处存放,所以要先确定设置的目录是事先存在的。
3. core文件产生促发条件
当程序接收到以下信号时会产生 core 文件:
SIGABRT :异常终止(abort)时发出的信号
说明:
调用abort函数时产生此信号。进程异常终止。
SIGBUS :硬件发生故障时发出的信号
说明:
指示一个实现定义的硬件故障。
SIGFPE :算术异常时发出的信号
说明:
此信号表示一个算术运算异常,例如除以0,浮点溢出等。
SIGILL :遇到非法硬件指令时发出的信号
说明:
此信号指示进程已执行一条非法硬件指令。4.3BSD由abort函数产生此信号。现在 abort() 函数用来生成 SIGABRT 信号。
SIGIOT :硬件故障时发出的信号
说明:
IOT这个名字来自于PDP-11对于 输入/输出 TRAP(input/output TRAP)指令的缩写。系统V的早期版本,由abort函数产生此信号。SIGABRT现在被用于此。
SIGQUIT :终端退出时发出的信号
说明:
当用户在终端上按退出键(一般采用Ctrl-\)时,产生此信号,并送至前台进程组中的所有进程。此信号不仅终止前台进程组(如SIGINT所做的那样),同时产生一个core文件。
SIGSEGV :无效存储访问发出的信号
说明:
进程进行了一次无效的存储访问。字SEGV表示“段违例(segmentation violation)”。
SIGSYS :无效的系统调用时发出的信号
说明:
进行了一个无效的系统调用。由于某种未知原因,进程执行了一条系统调用指令,但其指示系统调用类型的参数却是无效的。
SIGTRAP :硬件故障时发出的信号
说明:
此信号名来自于 PDP-11 的TRAP指令。
SIGXCPU :超过CPU限制(setrlimit)时发出的信号
说明:
SVR4 和 4.3+BSD 支持资源限制的概念。如果进程超过了其软 CPU 时间限制,则产生此信号。
XCPU 是 "exceeded CPU time“ 的缩写。
SIGXFSZ :超过文件长度限制(setrlimit)时发出的信号
说明:
如果进程超过了其软文件长度限制时发出此信号。
4. core文件调试
4.1 调试命令*
http://blog.csdn.net/shaovey/article/details/2744487
core文件需要使用gdb来查看。
gdb ./a.out
core-file core.xxxx
使用bt命令即可看到程序出错的地方。
以下两种命令方式具有相同的效果,但是在有些环境下不生效,所以推荐使用上面的命令。
1)gdb -core=core.xxxx
file ./a.out
bt
2)gdb -c core.xxxx
file ./a.out
bt
4.2 例子
写个简单的程序,看看core文件是不是会被产生。(http://blogger.org.cn/blog/more.asp?name=yach&id=22810)
$ more foo.c
#include
static void sub(void);
int main(void)
{
sub();
return 0;
}
static void sub(void)
{
int *p = NULL;
/* derefernce a null pointer, expect core dump. */
printf("%d", *p);
}
$ gcc -Wall -g foo.c
$ ./a.out
Segmentation fault
$ ls -l core.*
ls: core.*: No such file or directory
没有找到core文件,我们改改ulimit的设置,让它产生。1024是随便取的,要是core文件大于1024个块,就产生不出来了。
$ ulimit -c 1024
$ ulimit -a
core file size (blocks, -c) 1024
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) 4
max memory size (kbytes, -m) unlimited
open files (-n) 2048
pipe size (512 bytes, -p) 8
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 7168
virtual memory (kbytes, -v) unlimited
$ ./a.out
Segmentation fault (core dumped)
$ ls -l core.*
-rw------- 1 uniware uniware 53248 Jun 30 17:10 core.9128
注意看上述的输出信息,多了个(core dumped)。确实产生了一个core文件,9128是该进程的PID。我们用GDB来看看这个core。
$ gdb --core=core.9128
GNU gdb Asianux (6.0post-0.20040223.17.1AX)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-asianux-linux-gnu".
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x08048373 in ?? ()
(gdb) bt
#0 0x08048373 in ?? ()
#1 0xbfffd8f8 in ?? ()
#2 0x0804839e in ?? ()
#3 0xb74cc6b3 in ?? ()
#4 0x00000000 in ?? ()
此时用bt看不到backtrace,也就是调用堆栈,原来GDB还不知道符号信息在哪里。我们告诉它一下:
(gdb) file ./a.out
Reading symbols from ./a.out...done.
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) bt
#0 0x08048373 in sub () at foo.c:17
#1 0x08048359 in main () at foo.c:8
此时backtrace出来了。
(gdb) l
8 sub();
9 return 0;
10 }
11
12 static void sub(void)
13 {
14 int *p = NULL;
15
16 /* derefernce a null pointer, expect core dump. */
17 printf("%d", *p);
(gdb)
4.3 开发板上使用core文件调试
http://blog.csdn.net/shaovey/article/details/2744487
如果开发板的操作系统也是linux,core调试方法依然适用。如果开发板上不支持gdb,可将开发板的环境(依赖库)、可执行文件和core文件拷贝到PC的linux下。
在PC上调试开发板上产生的core文件,需要使用交叉编译器自带的gdb,并且需要在gdb中指定solib-absolute-prefix和solib-search-path两个变量以保证gdb能够找到可执行程序的依赖库路径。有一种建立配置文件的方法,不需要每次启动gdb都配置以上变量,即:在待运行gdb的路径下建立.gdbinit。
配置文件内容:
set solib-absolute-prefix YOUR_CROSS_COMPILE_PATH
set solib-search-path YOUR_CROSS_COMPILE_PATH
set solib-search-path YOUR_DEVELOPER_TOOLS_LIB_PATH
handle SIG32 nostop noprint pass
注意:待调试的可执行文件,在编译的时候需要加-g,core文件才能正常显示出错信息!有时候core信息很大,超出了开发板的空间限制,生成的core信息会残缺不全而无法使用,可以通过挂载到PC的方式来规避这一点。
5. 附录:
5.1 ulimit命令
1、说明:
ulimit用于shell启动进程所占用的资源.
2、类别:
shell内建命令
3、语法格式:
ulimit [-acdfHlmnpsStvw] [size]
4、参数介绍:
-H 设置硬件资源限制.
-S 设置软件资源限制.
-a 显示当前所有的资源限制.
-c size:设置core文件的最大值.单位:blocks
-d size:设置数据段的最大值.单位:kbytes
-f size:设置创建文件的最大值.单位:blocks
-l size:设置在内存中锁定进程的最大值.单位:kbytes
-m size:设置可以使用的常驻内存的最大值.单位:kbytes
-n size:设置内核可以同时打开的文件描述符的最大值.单位:n
-p size:设置管道缓冲区的最大值.单位:kbytes
-s size:设置堆栈的最大值.单位:kbytes
-t size:设置CPU使用时间的最大上限.单位:seconds
-v size:设置虚拟内存的最大值.单位:kbytes
5、简单实例:
在Linux下写程序的时候,如果程序比较大,经常会遇到“段错误”(segmentationfault)这样的问题,这主要就是由于Linux系统初始的堆栈大小(stack size)太小的缘故,一般为10M。我一般把stacksize设置成256M,这样就没有段错误了!命令为:
ulimit -s 262140
如果要系统自动记住这个配置,就编辑/etc/profile文件,在 “ulimit -S -c 0 > /dev/null 2>&1”行下,添加“ulimit -s 262140”,保存重启系统就可以了!
1] 在RH8的环境文件/etc/profile中,我们可以看到系统是如何配置ulimit的:
#grep ulimit /etc/profile
ulimit -S -c 0 > /dev/null 2>&1
这条语句设置了对软件资源和对core文件大小的设置
2]如果我们想要对由shell创建的文件大小作些限制,如:
#ll h
-rw-r--r-- 1 lee lee 150062 7月 22 02:39 h
#ulimit -f 100 #设置创建文件的最大块(一块=512字节)
#cat h>newh
File size limit exceeded
#ll newh
-rw-r--r-- 1 lee lee 51200 11月 8 11:47 newh
文件h的大小是150062字节,而我们设定的创建文件的大小是512字节x100块=51200字节. 当然系统就会根据你的设置生成了51200字节的newh文件.
3]可以像实例1]一样,把你要设置的ulimit放在/etc/profile这个环境文件中.
用途
设置或报告用户资源极限。
语法
ulimit [ -H ] [ -S ] [ -a ] [ -c ] [ -d ] [ -f ] [ -m ] [ -n ] [ -s ] [ -t ] [ Limit ]
描述
ulimit 命令设置或报告用户进程资源极限,如 /etc/security/limits 文件所定义。文件包含以下缺省值极限:
fsize = 2097151
core = 2097151
cpu = -1
data = 262144
rss = 65536
stack = 65536
nofiles = 2000
当新用户添加到系统中时,这些值被作为缺省值使用。当向系统中添加用户时,以上值通过 mkuser 命令设置,或通过 chuser 命令更改。极限分为软性或硬性。通过 ulimit 命令,用户可将软极限更改到硬极限的最大设置值。要更改资源硬极限,必须拥有 root 用户权限。
很多系统不包括以上一种或数种极限。 特定资源的极限在指定 Limit 参数时设定。Limit 参数的值可以是每个资源中指定单元中的数字,或者为值 unlimited。要将特定的 ulimit 设置为 unlimited,可使用词 unlimited。
注:
在 /etc/security/limits 文件中设置缺省极限就是设置了系统宽度极限, 而不仅仅是创建用户时用户所需的极限。
省略 Limit 参数时,将会打印出当前资源极限。除非用户指定 -H 标志,否则打印出软极限。当用户指定一个以上资源时,极限名称和单元在值之前打印。如果未给予选项,则假定带有了 -f 标志。
由于 ulimit 命令影响当前 shell 环境,所以它将作为 shell 常规内置命令提供。如果在独立的命令执行环境中调用该命令,则不影响调用者环境的文件大小极限。以下示例中正是这种情况:
nohup ulimit -f 10000
env ulimit 10000
一旦通过进程减少了硬极限,若无 root 特权则无法增加,即使返回到原值也不可能。
关于用户和系统资源极限的更多信息,请参见 AIX 5L Version 5.3 Technical Reference: BaseOperating System and Extensions Volume 1 中的 getrlimit、setrlimit 或 vlimit 子例程。
标志
-a 列出所有当前资源极限。
-c 以 512 字节块为单位,指定核心转储的大小。
-d 以 K 字节为单位指定数据区域的大小。
-f 使用 Limit 参数时设定文件大小极限(以块计),或者在未指定参数时报告文件大小极限。缺省值为 -f 标志。
-H 指定设置某个给定资源的硬极限。如果用户拥有 root 用户权限,可以增大硬极限。任何用户均可减少硬极限。
-m 以 K 字节为单位指定物理存储器的大小。
-n 指定一个进程可以拥有的文件描述符的数量的极限。
-s 以 K 字节为单位指定堆栈的大小。
-S 指定为给定的资源设置软极限。软极限可增大到硬极限的值。如果 -H 和 -S 标志均未指定,极限适用于以上二者。
-t 指定每个进程所使用的秒数。
退出状态
返回以下退出值:
0 成功完成。
>0 拒绝对更高的极限的请求,或发生错误。
示例
要将文件大小极限设置为 51,200 字节,输入:
ulimit -f 100