文中提到:
大多数UNIX调试程序都使用core文件以检查进程终止时的状态。
如书上表10-1所示,当进程接收到例如SIGABRT(异常终止)、SIGBUS(硬件故障)、SIGSEGV(无效内存引用)等信号的时候会产生相应的core文件,该core文件复制了进程的存储镜像。在运行程序的时候,我们会经常遇到Segmentation fault,也就是段错误,表示该进程进行了一次无效的内存访问。对于此类情况,我们很难发现问题具体是出现在程序的哪一行,如果是一小段程序,我们可以直接用gdb一步一步跟踪调试,但是如果程序规模很大,这样做显然就不实现了。回过头来,我们发现对于此类错误,程序会接收到SIGSEGV(无效内存引用)信号,而该信号在终止程序的同时,会在当前目录产生一个相应的core文件。因此,我们可以让gdb根据该core文件分析中断的位置,也就是出现该错误的位置。首先,我们应该现查看当前系统是否开启了core文件支持(以GNU/Linux为例):
- ~ $ ulimit -c
- 0
- ~ $ ulimit -c unlimited
- ~ $ ulimit -c
- unlimited
- 默认情况下,core dump生成的文件名为core,而且就在程序当前目录下。新的core会覆盖已存在的core。通过修改/proc/sys/kernel/core_uses_pid文件,可以将进程的pid作为作为扩展名,生成的core文件格式为core.xxx,其中xxx即为pid 方法:sudo echo "1" > /proc/sys/kernel/core_uses_pid 通过修改/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件格式。例如:将所有的core文件生成到/corefile目录下,文件名的格式为core-命令名-pid-时间戳. 方法:sudo echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
0表示未开启,大于0的表示默认core文件的大小,如果超过此大小,则截断,这会造成core文件的信息不完整,因此我们这里将其设置为unlimited,也就是无限大。接下来,我们写一段C代码来测试:
- include <stdio.h>
- char *str;
- void core_test()
- {
- printf("%c\n",*str);
- }
- int main(int argc, char *argv[])
- {
- core_test();
- return 0;
- }
然后,依次进行编译(无警告无错误),运行(段错误),并结合产生的core文件调试:
- ~ $ gcc -o test test.c -g
- ~ $ ./test
- Segmentation fault (core dumped)
- ~ $ file core
- core: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from './test'
- ~ $ gdb test core
- GNU gdb (Gentoo 7.3.1 p1) 7.3.1
- Copyright (C) 2011 Free Software Foundation, Inc.
- ...
- Core was generated by `./test'.
- Program terminated with signal 11, Segmentation fault.
- 0 0x08048574 in core_test () at test.c:7
- 7 printf("%c\n",*str);
- (gdb) where
- 0 0x08048574 in core_test () at test.c:7
- 1 0x0804859d in main (argc=1, argv=0xbfed0214) at test.c:12
- (gdb) quit
很明显,问题是出在这里:
- 0 0x08048574 in core_test () at test.c:7
- 7 printf("%c\n",*str);
字符指针str未经初始化就试图(解除引用)访问它所指向的内存空间,导致出现无效的内存访问。其中where命令用来查看当前调用栈信息。