Linux下程序产生“段错误”的原因及其解决办法

Linux下程序产生“段错误”的原因及其解决办法


        产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址。

        一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,它是一个48位的寄存器,其中的32位是保 存由它指向的gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序在cpu中的运行级别,指向的gdt是以64位为一个单 位的表,在这张表中就保存着程序运行的代码段、数据段的起始地址、与此相应的段限和页面交换、程序运行级别还有内存粒度等等的信息。一旦一个程序发生了越 界访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了.

        在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的:

1)访问系统数据区,尤其是往系统保护的内存地址写数据,最常见的就是给一个指针以0地址。
2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域。另外,缓存溢出也可能引起“段错误”,对于这种while(1) {do}的程序,这个问题最容易发生,多此sprintf或着strcat有可能将某个buff填满,溢出,所以每次使用前,最好memset一下,不过 要是一开始就是段错误,而不是运行了一会儿出现的,缓存溢出的可能性就比较小。 
3 多线程程序使用了线程不安全的函数。4多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump .
5  随 意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的 指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时 就很容易因为bus error而core dump。

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

解决方法

1.利用gdb逐步查找段错误:


        这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g -rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:

xiaosuo@gentux test $ gcc -g -rdynamic d.c
xiaosuo@gentux test $ gdb ./a.out
GNU gdb 6.5
Copyright (C) 2006 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 "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) r
Starting program: /home/xiaosuo/test/a.out

Program received signal SIGSEGV, Segmentation fault.
0x08048524 in dummy_function () at d.c:4
4 *ptr = 0x00;
(gdb)

  好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。


从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal),我们知道SIGSEGV默认的处理动作是打印“段错误”的出错信息,并产生Core文件,由此我们又产生了方法二。

2.分析Core文件:
发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.
 gdb [exec file] [core file]  


Core文件是什么呢?

The default action of certain signals is to cause a process to terminate and produce a core dump file, a disk file containing an image of the process's memory at the time of termination. A list of the signals which cause a process to dump core can be found in signal(7).

     以上资料摘自man page(man 5 core)。不过奇怪了,我的系统上并没有找到core文件。后来,忆起为了减少系统上的拉圾文件的数量(本人有些洁癖,这也是我喜欢Gentoo的原因 之一),禁止了core文件的生成,查看了以下果真如此,将系统的core文件的大小限制在512K大小,再试:

xiaosuo@gentux test $ ulimit -c
0
xiaosuo@gentux test $ ulimit -c 1000
xiaosuo@gentux test $ ulimit -c
1000
xiaosuo@gentux test $ ./a.out
段错误 (core dumped)
xiaosuo@gentux test $ ls
a.out core d.c f.c g.c pango.c test_iconv.c test_regex.c
 

配置操作系统使其产生core文件

首 先通过ulimit命令查看一下系统是否配置支持了dump core的功能。通过ulimit -c或ulimit -a,可以查看core file大小的配置情况,如果为0,则表示系统关闭了dump core。可以通过ulimit -c unlimited来打开。若发生了段错误,但没有core dump,是由于系统禁止core文件的生成。

解决方法:
$ulimit -c unlimited  (只对当前shell进程有效)
或在~/.bashrc 的最后加入: ulimit -c unlimited (一劳永逸)

# ulimit -c

unlimited

$ ulimit -a

time(seconds)        unlimited

file(blocks)         unlimited

data(kb)             unlimited

stack(kb)            8192

coredump(blocks)     unlimited

memory(kb)           unlimited

locked memory(kb)    64

process              1394

nofiles              1024

vmemory(kb)          unlimited

locks                unlimited 

  

  core文件终于产生了,用gdb调试一下看看吧:

xiaosuo@gentux test $ gdb ./a.out core
GNU gdb 6.5
Copyright (C) 2006 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 "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".


warning: Can't read pathname for load map: 输入/输出错误.
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
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x08048524 in dummy_function () at d.c:4
4 *ptr = 0x00;

  还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。
接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。

  Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了:


3.段错误时启动调试:


void dump(int signo)
{
char buf[1024];
char cmd[1024];
FILE *fh;

snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());
if(!(fh = fopen(buf, "r")))
exit(0);
if(!fgets(buf, sizeof(buf), fh))
exit(0);
fclose(fh);
if(buf[strlen(buf) - 1] == 'n')
buf[strlen(buf) - 1] = '';
snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());
system(cmd);

exit(0);
}

void
dummy_function (void)
{
unsigned char *ptr = 0x00;
*ptr = 0x00;
}

int
main (void)
{
signal(SIGSEGV, &dump);//SIGSEGV是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号
dummy_function ();

return 0;
}

  编译运行效果如下:

xiaosuo@gentux test $ gcc -g -rdynamic f.c
xiaosuo@gentux test $ ./a.out
GNU gdb 6.5
Copyright (C) 2006 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 "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

Attaching to program: /home/xiaosuo/test/a.out, process 9563
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
0xffffe410 in __kernel_vsyscall ()
(gdb) bt
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xb7ee4b53 in waitpid () from /lib/libc.so.6
#2 0xb7e925c9 in strtold_l () from /lib/libc.so.6
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内存泄漏是指在程序执行过程中,分配的内存未能被正确释放,导致内存占用逐渐增加的问题。 造成Linux内存泄漏的原因主要有以下几点: 1. 错误的内存分配和释放:程序使用malloc或new等函数分配了一块内存,但忘记了使用free或delete等函数释放内存。 2. 循环引用:在使用动态内存管理的情况下,对象之间可能存在循环引用,当对象间的引用关系无法被断开时,这些对象将无法被垃圾回收,导致内存泄漏。 3. 文件描述符未关闭:文件打开后未进行适当的关闭,导致系统资源无法释放。 4. 缓存未释放:程序中使用了缓存,但未在适当的时候进行清理,导致缓存占用大量内存。 解决Linux内存泄漏的办法包括以下几点: 1. 定期检查和审查代码:开发人员应该进行定期的代码审查,确保内存的正确释放。对于使用动态内存分配的情况,应该在分配内存后及时释放,避免出现忘记释放内存的情况。 2. 使用内存管理工具:Linux提供了一些内存分析工具,例如Valgrind等,可以帮助检测和诊断内存泄漏问题。开发人员可以使用这些工具来定位内存泄漏的具体位置。 3. 注意文件描述符的关闭:对于打开的文件描述符,确保在使用完毕后进行适当的关闭操作,避免资源泄漏。 4. 缓存管理:程序中使用缓存时,需要在合适的时机进行清理,避免缓存占用过多内存。 总之,对于Linux内存泄漏问题,开发人员应该注重代码的规范性和内存管理的正确性,加强代码审查和使用合适的工具来帮助定位和解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值