5.1 检查核心文件
为了允许程序在调试器下运行,'-g'选项的另一个作用是帮助查找程序错误的地方。
当一个程序异常退出时操作系统会创建一个核心文件通常名为'core',它包含在异常退出时程序在内存中的状态。结合'-g'选项产生的符号表信息,核心文件可以用于查找程序停止的地方和此时程序变量的值。这对于软件开发和调试都非常有用。
这有一个简单的程序包含一个内存访问bug,我们使用这个程序产生核心文件:
int a (int *p);
int
main (void)
{
int p = 0; / null pointer */
return a (p);
}
int
a (int *p)
{
int y = *p;
return y;
}
程序试图定义一个空指针p,这是一个无效的操作。在大多数操作系统上这会导致错误。
为了能够找到错误的地方,我们需要使用'-g'选项编译此程序:
$ gcc -Wall -g null.c
注意空指针只会在运行时刻产生错误所以'-Wall'选项不会产生任何警告信息。
在x86 GNU/Linux系统上运行这个可执行文件会导致操作系统异常终止此程序:
$ ./a.out
Segmentation fault (core dumped)
当错误信息'core dumped'显示出来时操作系统会在当前目录下产生一个名为'core'的文件。这个核心文件包含了程序终止时使用所有内存页的一个完全拷贝。有时候段错误的原因就是程序试图访问一个受限制的内存段。
一些系统被设置为默认情况下不产生核心文件,因为这个文件可以很大致使系统的硬盘被填满。在GNU命令行中'ulimit -c'命令控制核心文件的最大容量限制。如果容量限制为零则不会产生核心文件。当前容量限制可以使用下面的命令行查看:
$ ulimit -c
0
如果结果是0就像上面显示的一样,则可以使用下面的命令增加核心文件为任意大小:
$ ulimit -c unlimited
注意这个设置只会在当前shell下有效。为了永久有效可以将命令写入登陆文件,例如GNU Bash shell中的'.bash_profile'文件。
核心文件可以使用下面命令加载到GNU调试器gdb中:
$ gdb EXECUTABLE-FILE CORE-FILE
注意只有可执行文件和核心文件同时存在时才可以进行调试--不可能只使用核心文件进行调试。在这个例子中,我们可以使用下面命令将可执行文件和核心文件加载到gdb中:
$ gdb a.out core
调试器立即开始打印诊断细信息,然后显示程序终止处的代码行:
$ gdb a.out core
Core was generated by ‘./a.out’.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0 0x080483ed in a (p=0x0) at null.c:13
13 int y = *p;
(gdb)
最后一行是GNU调试器的提示符--它指示更多命令可以在这里输入。
为了检查异常终止的原因,我们可以使用调试器命令print显示指针p的值:
(gdb) print p
$1 = (int *) 0x0
它显示了p是一个'int '类型的空指针(0x0),所以我们可以知道p指针的定义错误导致了程序异常终止。