• segment fault 段错误 (core dumped)的起因分析(转)


    内核使用内存描述符结构体表示进程的地址空间,该结构体包含了和进程地址空间有关的全部信息。内存描述符由mm_struct结构体表示,定义在文件<linux/sched.h>中。进程地址空间由每个进程的线性地址区(vm_area_struct)组成。通过内核,进程可以给自己的地址空间动态的添加或减少线性区域。

     

    mm_users域记录正在使用该地址的进程数目。比如,如果两个进程共享该地址空间,那么mm_users的值便等于2;mm_count域是mm_struct的结构体的主引用计数,只要 mm_users不为0,那么mm_count值就等于1.当mm_users值减为0(两个线程都退出)时,mm_count域的值才变为0。如果mm_count的值等于0,说明已经没有任何指向该mm_struct结构 体的引用了,这时该结构体会被销毁。mmap和mm_rb这两个不同的数据结构描述的对象是相同的:该地址空间中的全部内存区域。

    而fork系统调用产生的子进程中的mm_struct结构体实际是通过文件kernel/fork.c中的alloc_mm()宏从mm_cachep slab缓存中分配得到的。通常,每个进程都有一个唯一的 mm_struct结构体,即唯一的进程地址空间。是否共享地址空间,几乎是进程和Linux中所谓线程间本质上的唯一区别。

    进程就是通过mm_struct 来描述3G的线性地址空间,mm_struct通过

    划分为一个个线性区,而每个线性区用一个 vm_area_struct来描述,其实我们的mm_struct是与我们的可执行文件的结构体相关联的,首先看我们的mm_struct:

    从上面可以看出mm_struct 包含了进程start_code,end_code,start_data,end_data等section段描述的结构,我们来看一个可执行文件的构成来看看,就拿上面的hello

    [qiqi@localhost test]$ objdump -h hello

    hello: file format elf32-i386

    Sections:
    Idx Name Size VMA LMA File off Algn
    0 .interp 00000013 08048134 08048134 00000134 2**0

    VMA代表虚拟地址,这里表示我们的程序运行是的虚拟地址起为 0x08048134

      CONTENTS, ALLOC, LOAD, READONLY, DATA
      1 .note.ABI-tag 00000020  08048148  08048148  00000148  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      ......
     10 .init         00000030  080482f4  080482f4  000002f4  2**2          init加载头最开始执行的
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     11 .plt          00000070  08048324  08048324  00000324  2**2       跳转我们程序执行的头
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     12 .text         000001cc  080483a0  080483a0  000003a0  2**4   ..即我们的代码段
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     13 .fini         0000001c  0804856c  0804856c  0000056c  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     14 .rodata       00000018  08048588  08048588  00000588  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
    ....
     20 .dynamic      000000c8  08049654  08049654  00000654  2**2 
                      CONTENTS, ALLOC, LOAD, DATA
     21 .got          00000004  0804971c  0804971c  0000071c  2**2
                      CONTENTS, ALLOC, LOAD, DATA
    ....
     23 .data         00000004  08049744  08049744  00000744  2**2            数据段
                      CONTENTS, ALLOC, LOAD, DATA
     24 .bss          00000008  08049748  08049748  00000748  2**2         未初始化段
                      ALLOC
     25 .comment      0000002c  00000000  00000000  00000748  2**0    未初始化的符号表
                      CONTENTS, READONLY

    由链接过程可以看出main并不是真正的入口,是从.plt段中跳转过来的所有的程序都是从这里进入的,有一相当于jmp *main的跳转指令,

    同时可以看出我们的可执行文件被分成以一个不同的section,这就和我们mm_struct中的线性区描述符对应起来了,比如我们的.data段就被加载到start_data与end_data地址空间的地方,其它的段与此类似!!!!!!,

    那么为什么我们产生segment fault 呢,???因为我们访问的地址没有映射啊,!!!就是地址没有在mm_struct中描述的空间中,因为我们的

    mm_struct并没有映射整个3G的空间啊,因为我们程序的每个段都是有大小限制的,.data,.text,.bss,.common等已经指名的section以外,

    还有两个非常重要的线性区start_stack,end_stack(栈)与start_brk,end_brk(堆),如果我们访问的线性地址没有在这些地址空间中,那么就会产生segment fault,



    造成程序core dump的原因很多,这里根据以往的经验总结一下:

    1 内存访问越界

      a) 由于使用错误的下标,导致数组访问越界

      b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符

      c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。

     

    2 多线程程序使用了线程不安全的函数。

    应该使用下面这些可重入的函数,尤其注意红色标示出来的函数,它们很容易被用错:

    asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

     

    3 多线程读写的数据未加锁保护。

    对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump

     

    4 非法指针

      a) 使用空指针

      b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump.

     

    5 堆栈溢出

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

    转自:https://blog.csdn.net/melong100/article/details/6433047

    https://blog.csdn.net/bzhxuexi/article/details/17840941

  • 相关阅读:
    Codeforces Round #693 (Div. 3) G. Moving to the Capital (图,dp)
    Educational Codeforces Round 102 (Rated for Div. 2) B. String LCM (构造,思维)
    Hadoop离线计算——环境搭建(一)
    大数据项目开发进度(实时更新)
    【迭代式开发】v1架构设计文档——大数据开发实战项目(三)
    【迭代式开发】V1软件需求规格说明书——大数据开发实战项目(二)
    Flume安装——环境搭建(二)
    【中英双语】Spark官方文档解读(一)——Spark概述
    TortoiseSVN使用教程【多图超详细】——大数据开发实习day1
    【深度学习TPU+Keras+Tensorflow+EfficientNetB7】kaggle竞赛 使用TPU对104种花朵进行分类 第十八次尝试 99.9%准确率
  • 原文地址:https://www.cnblogs.com/zl1991/p/16344534.html
Copyright © 2020-2023  润新知