`
yzd
  • 浏览: 1814979 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

linux下编程的段错误的解决办法

 
阅读更多
<p>简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址.<br><br>一般来说, 段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的 gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。一旦一个程序发生了越界访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了.<br><br>在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的<br><br><span style="color: #0000ff;">1)访问系统数据区,尤其是往 系统保护的内存地址写数据<br> 最常见就是给一个指针以0地址<br>2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域</span><br><br>解决方法<br><br>我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工“除虫”(debug),往往是效率低下且让人厌烦的,本文将就"段错误"这个内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。<br>下面将就以下的一个存在段错误的程序介绍几种调试方法:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td> 1 dummy_function (void)<br> 2 {<br> 3 unsigned char *ptr = 0x00;<br> 4 *ptr = 0x00;<br> 5 }<br> 6<br> 7 int main (void)<br> 8 {<br> 9 dummy_function ();<br> 10<br> 11 return 0;<br> 12 }<br>
</td>
</tr></tbody></table>
<p>作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。我们尝试编译运行它:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $ ./a.out<br>段错误<br>
</td>
</tr></tbody></table>
<p>果然不出所料,它出错并退出了。<br><span style="font-weight: bold;">1.利用gdb逐步查找段错误:</span><br>这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g -rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $ gcc -g -rdynamic d.c<br>xiaosuo@gentux test $ gdb ./a.out<br>GNU gdb 6.5<br>Copyright (C) 2006 Free Software Foundation, Inc.<br>GDB is free software, covered by the GNU General Public License, and you are<br>welcome to change it and/or distribute copies of it under certain conditions.<br>Type "show copying" to see the conditions.<br>There is absolutely no warranty for GDB. Type "show warranty" for details.<br>This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br><br>(gdb) r<br>Starting program: /home/xiaosuo/test/a.out<br><br>Program received signal SIGSEGV, Segmentation fault.<br>0x08048524 in dummy_function () at d.c:4<br>4 *ptr = 0x00;<br>(gdb)<br>
</td>
</tr></tbody></table>
<p>哦?!好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。<br>从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal),我们知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件,由此我们又产生了方法二。<br><span style="font-weight: bold;">2.分析Core文件:</span><br>Core文件是什么呢?</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>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).</td>
</tr></tbody></table>
<p>以 上资料摘自man page(man 5 core)。不过奇怪了,我的系统上并没有找到core文件。后来,忆起为了渐少系统上的拉圾文件的数量(本人有些洁癖,这也是我喜欢Gentoo的原因之一),禁止了core文件的生成,查看了以下果真如此,将系统的core文件的大小限制在512K大小,再试:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $ ulimit -c<br>0<br>xiaosuo@gentux test $ ulimit -c 1000<br>xiaosuo@gentux test $ ulimit -c<br>1000<br>xiaosuo@gentux test $ ./a.out<br>段错误 (core dumped)<br>xiaosuo@gentux test $ ls<br>a.out core d.c f.c g.c pango.c test_iconv.c test_regex.c<br>
</td>
</tr></tbody></table>
<p>core文件终于产生了,用gdb调试一下看看吧:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $ gdb ./a.out core<br>GNU gdb 6.5<br>Copyright (C) 2006 Free Software Foundation, Inc.<br>GDB is free software, covered by the GNU General Public License, and you are<br>welcome to change it and/or distribute copies of it under certain conditions.<br>Type "show copying" to see the conditions.<br>There is absolutely no warranty for GDB. Type "show warranty" for details.<br>This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br><br><br>warning: Can't read pathname for load map: 输入/输出错误.<br>Reading symbols from /lib/libc.so.6...done.<br>Loaded symbols for /lib/libc.so.6<br>Reading symbols from /lib/ld-linux.so.2...done.<br>Loaded symbols for /lib/ld-linux.so.2<br>Core was generated by `./a.out'.<br>Program terminated with signal 11, Segmentation fault.<br>#0 0x08048524 in dummy_function () at d.c:4<br>4 *ptr = 0x00;<br>
</td>
</tr></tbody></table>
<p>哇,好历害,还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。<br>接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。<br>Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了:<br><span style="font-weight: bold;">3.段错误时启动调试:</span></p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;signal.h&gt;<br>#include &lt;string.h&gt;<br><br>void dump(int signo)<br>{<br> char buf[1024];<br> char cmd[1024];<br> FILE *fh;<br><br> snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());<br> if(!(fh = fopen(buf, "r")))<br> exit(0);<br> if(!fgets(buf, sizeof(buf), fh))<br> exit(0);<br> fclose(fh);<br> if(buf[strlen(buf) - 1] == '/n')<br> buf[strlen(buf) - 1] = '/0';<br> snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());<br> system(cmd);<br><br> exit(0);<br>}<br><br> void<br>dummy_function (void)<br>{<br> unsigned char *ptr = 0x00;<br> *ptr = 0x00;<br>}<br><br> int<br>main (void)<br>{<br> signal(SIGSEGV, &amp;dump);<br> dummy_function ();<br><br> return 0;<br>}<br>
</td>
</tr></tbody></table>
<p>编译运行效果如下:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $ gcc -g -rdynamic f.c<br>xiaosuo@gentux test $ ./a.out<br>GNU gdb 6.5<br>Copyright (C) 2006 Free Software Foundation, Inc.<br>GDB is free software, covered by the GNU General Public License, and you are<br>welcome to change it and/or distribute copies of it under certain conditions.<br>Type "show copying" to see the conditions.<br>There is absolutely no warranty for GDB. Type "show warranty" for details.<br>This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br><br>Attaching to program: /home/xiaosuo/test/a.out, process 9563<br>Reading symbols from /lib/libc.so.6...done.<br>Loaded symbols for /lib/libc.so.6<br>Reading symbols from /lib/ld-linux.so.2...done.<br>Loaded symbols for /lib/ld-linux.so.2<br>0xffffe410 in __kernel_vsyscall ()<br>(gdb) bt<br>#0 0xffffe410 in __kernel_vsyscall ()<br>#1 0xb7ee4b53 in waitpid () from /lib/libc.so.6<br>#2 0xb7e925c9 in strtold_l () from /lib/libc.so.6<br>#3 0x08048830 in dump (signo=11) at f.c:22<br>#4 &lt;signal handler called&gt;<br>#5 0x0804884c in dummy_function () at f.c:31<br>#6 0x08048886 in main () at f.c:38<br>
</td>
</tr></tbody></table>
<p>怎么样?是不是依旧很酷?<br>以上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h(这些函数都没有提供man page,难怪我们找不到),另外你也可以通过<a href="http://writeblog.csdn.net/link.php?url=http://www.gnu.org%2Fsoftware%2Flibc%2Fmanual%2Fhtml_node%2FBacktraces.html" target="_blank"><span style="color: #000033;"><span style="text-decoration: underline;">gnu的手册</span></span></a>进行学习。<br><span style="font-weight: bold;"><span style="color: #ff0000;">4.利用backtrace和objdump进行分析:</span></span><br>重写的代码如下:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>#include &lt;execinfo.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;signal.h&gt;<br><br>/* A dummy function to make the backtrace more interesting. */<br> void<br>dummy_function (void)<br>{<br> unsigned char *ptr = 0x00;<br> *ptr = 0x00;<br>}<br><br>void dump(int signo)<br>{<br> void *array[10];<br> size_t size;<br> char **strings;<br> size_t i;<br><br> size = backtrace (array, 10);<br> strings = backtrace_symbols (array, size);<br><br> printf ("Obtained %zd stack frames./n", size);<br><br> for (i = 0; i &lt; size; i++)<br> printf ("%s/n", strings[i]);<br><br> free (strings);<br><br> exit(0);<br>}<br><br> int<br>main (void)<br>{<br> signal(SIGSEGV, &amp;dump);<br> dummy_function ();<br><br> return 0;<br>}<br>
</td>
</tr></tbody></table>
<p>编译运行结果如下:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $ gcc -g -rdynamic g.c<br>xiaosuo@gentux test $ ./a.out<br>Obtained 5 stack frames.<br>./a.out(dump+0x19) [0x80486c2]<br>[0xffffe420]<br>./a.out(main+0x35) [0x804876f]<br>/lib/libc.so.6(__libc_start_main+0xe6) [0xb7e02866]<br>./a.out [0x8048601]<br>
</td>
</tr></tbody></table>
<p>这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:</p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>xiaosuo@gentux test $<span style="color: #ff0000;"> objdump -d a.out</span><br>
</td>
</tr></tbody></table>
<p></p>
<table style="width: 80%; font-size: 12px; border: #999999 1px solid;" border="0" align="center"><tbody><tr>
<td>8048765: e8 02 fe ff ff call 804856c &lt;signal@plt&gt;<br>804876a: e8 25 ff ff ff call 8048694 &lt;dummy_function&gt;<br><span style="color: #ff0102;">804876f</span>: b8 00 00 00 00 mov $0x0,%eax<br>8048774: c9 leave<br>
</td>
</tr></tbody></table>
<p>我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!<br><span style="font-weight: bold;">后记:</span><br>本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。</p>
分享到:
评论

相关推荐

    Linux系统编程之线程同步

    “同步”的目的,是为了避免数据混乱,解决与时间有关的错误。实际上,不仅线程间需要同步,进程间、信号间等等都需要同步机制。 因此,所有“多个控制流,共同操作一个共享资源”的情况,都需要同步。 数据混乱...

    linux下使用IIC总线读写EEPROM

    2, 本文给出了在编程中遇到的几种非常隐蔽的错误的解决方法。 3,本文的读写程序非常通用: i2c -d /dev/i2c-1 -s 0x51 0x05 18 -----Write 18 to the register: 0x05 of the i2c-slave address: 0x51 i2c -d /dev/...

    详解linux中 Nginx 常见502错误问题解决办法

    常见的Nginx 502 Bad Gateway解决办法如下: Nginx 502错误情况1: 网站的访问量大,而php-cgi的进程数偏少。 针对这种情况的502错误,只需增加php-cgi的进程数。具体就是修改/usr/local/php/etc/php-fpm.conf ...

    深入理解程序设计使用Linux汇编语言

    《深入理解程序设计:使用Linux汇编语言》介绍了Linux平台下的汇编语言编程,教你从计算机的角度看问题,从而了解汇编语言及计算机的工作方式,为成就自己的优秀程序员之梦夯实基础。 很多人都认为汇编语言晦涩难懂...

    关于在linux下出现stdio.h文件不存在问题的解决方法

    这个时候按道理能够成功解决,但是却出现如下错误: root@chenxiaojian:/etc/apt# apt-get install libc6-dev 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完成 有一些软件包无法被...

    网络编程实用教程(第三版).zip

    2.1.3 套接字编程接口在Windows和Linux操作系统中得到继承和发展 25 2.1.4 套接字编程接口的两种实现方式 25 2.1.5 套接字通信与UNIX操作系统的输入/输出的关系 26 2.2 套接字编程的基本概念 27 2.2.1 什么...

    Linux多线程服务端编程:使用muduo C++网络库

    《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...

    RED HAT LINUX 6大全

    第四部分为Linux编程;第五部分为附录。本书内容翔实、涉及领域广泛,并且提供了详细的例子和大量的参考资料(包括书籍、电子文档和Internet站点),是一本学习、使用和管理Linux不可多得的好书。 目 录 译者序 前言...

    LINUX安装与配置简明手册

    本书详细介绍如何在个人电脑上安装配置Caldera和Red Hat两种发行版本的Linux操作系统,并能帮助用户解决安装和配置过程中出现的各种问题。本书既能指导你进行基本的安装步骤,也能指导你定制独具特色的Linux 操作...

    错误:sem_union的存储大小未知问题的解决方法

    今天在编译代码的时候提示 错误: ‘sem_union’的存储大小未知 ...Linux下编译时出现的错误及解决方法 (1)由于是Linux新手,所以现在才开始接触线程编程,照着GUN/Linux编程指南中的一个例子输入编译,结果出现

    linux安装与配置简明手册

    本书详细介绍如何在个人电脑上安装配置Caldera和Red Hat两种发行版本的Linux操作系统,并能帮助用户解决安装和配置过程中出现的各种问题。本书既能指导你进行基本的安装步骤,也能指导你定制独具特色的Linux 操作...

    入门学习Linux常用必会60个命令实例详解doc/txt

    本文以Mandrake Linux 9.1(Kenrel 2.4.21)为例,介绍Linux下的安装和登录命令。 immortality按:请用ctrl+f在本页中查找某一部分的内容或某一命令的用法。 ----------------------------------------------------...

    操作系统实验的教程与指导课件

    Unix 和Linux 操作系统环境下的系统编程以及 Windows编程API 时间分配 讲授 16 上机实验 16 成绩考查方法 上机作业和实验报告 计算机系统的组成 操作系统的定义 操作系统的功能和特征 任何计算机系统都包含一个名...

    解决Python3.8用pip安装turtle-0.0.2出现错误问题

    turtle库是python的基础绘图库,这个库被介绍为一个最常用的用来给孩子们介绍编程知识的方法库,这篇文章主要介绍了解决Python3.8用pip安装turtle-0.0.2出现错误问题,需要的朋友可以参考下

    NTKO文档在线编辑控件4.0.1.2

    9 广泛的操作系统,Web服务器,数据库和编程语言支持 后台支持Windows,Linux,Unix等各种操作系统;支持IIS,Domino,Websphere,Apache等所有后台WEB服务器类型,支持Db2,Oracle,MySQL,SQL Server等各种常用...

    undefined reference to ‘pthread_create’的解决方法

    照着GUN/Linux编程指南中的一个例子输入编译,结果出现如下错误: undefined reference to ‘pthread_create’undefined reference to ‘pthread_join’ 问题原因: pthread 库不是 Linux 系统默认的库,连接时需要...

    bashjs:linux命令行功能的实现

    编写 bash 脚本是完成工作的好方法,并且可能是任何编程语言都无法比拟的。 但是在为不同目的编写大量 bash 脚本时,我遇到了一些 bash 几乎无法解决的问题,例如 Bash 不是一种编程语言,即循环、条件、变量、数学...

    Android高级编程--源代码

    通过学习,您可以打下牢固的理论根基,了解使用当前android 1.0 sdk编写定制移动程序所需的知识,还能灵活快捷地运用未来的增强功能构建最前沿的解决方案。  主要内容  ◆android移动开发的最佳实践  ◆简要介绍...

    程序设计入门—C语言(完)-浙江大学-翁恺 视频.txt

    对于非计算机专业而言,程序设计的学习有助于理解计算机的能力所在,理解哪些是计算机擅长解决的问题,怎样的方式方法是计算机擅长的手段,从而能更好地利用计算机来解决本专业领域内的问题。 C语言是古老而长青的...

Global site tag (gtag.js) - Google Analytics