精华内容
下载资源
问答
  • 段错误 万次阅读 多人点赞
    2019-05-29 16:16:21

    1.段错误的定义

          段错误是计算机软件运行过程中可能出现的一种特殊错误情况。当程序试图访问不允许访问的内存位置,或试图以不允许的方式访问内存位置(例如尝试写入只读位置,或覆盖部分操作系统)时会发生段错误。分段是操作系统内存管理和保护的一种方法。在大多数情况下,它已经被分页所取代,但是分段的许多术语仍然被使用,“分段错误”就是一个例子。尽管分页被用作主内存管理策略,但有些操作系统在某些逻辑级别上仍然有分段。在类Unix操作系统上,访问无效内存的进程接收SIGSEGV信号。在Microsoft Windows上,访问无效内存的进程会收到状态“访问冲突”异常。段错误的原因(这篇文章非常好)

    2.段错误的常见原因

    2.1 使用未经初始化及或已经释放的指针地址

    • 手动释放地址:
    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    	int a = 555;
    	int *str = &a;
    	*str = 666;
    	str = NULL;
    	printf("str ==%d", *str);
    	system("pause");
    	return 0;
    }
    •  被动释放的地址:

            地址会被释放,但不一定会崩溃。

    #include <stdio.h>
    #include <stdlib.h>
    char *getStr() {
    	char str[] = "hello world";
    	return str;
    }
    int main() {
    	char *str;
    	str = getStr();
    	printf("str == %s",str);
    	system("pause");
    	return 0;
    }
    • 释放无效指针
    #include <stdlib.h>
    
    int main()
    {
        char *buffer = malloc(20);
        free(buffer+2);
    
        return 0;
    }

    2.2 访问受系统保护的内存地址

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    	int *str = (int *)0x20000000;
    	*str = 555;
    	system("pause");
    	return 0;
    }

    2.3  写入只读的内存地址

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    	char *str = "samson";
    	*str = "cool";
    	printf("name is %d",str);
    	system("pause");
    	return 0;
    }

    2.3 数组越界

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    	int arr[10] = { 0 };
    	int i = 0;
    	for (i = 0; i < 100; i++) {
    		arr[i] = 6;
    	}
    	printf("%p",arr);
    	system("pause");
    	return 0;
    }

    2.4 堆栈溢出

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    	int i = 0;
    	for (; i < 10000; i++) {
    		int arr[999666];
    	}
    	system("pause");
    	return 0;
    }

    2.5 文件操作符超出限制

    #include <stdio.h>
    #include <assert.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    int main(int argc, char *argv[]) {
            for (int i = 0; i < 1025; i++) {
                int fpr, fpw,res;
                int buf[100];
                fpr = open(argv[1], O_RDONLY, 0600);
                fpw = open(argv[2], O_WRONLY|O_CREAT, 0600);
                assert(fpr != -1 && fpw != -1);
                memset(buf, 0, sizeof(buf));
                while((res = read(fpr, buf, 100)) > 0) {
                    write(fpw, buf, res);
    
                }
            }
    
            return 0;
    }
    

    2.6 跨线程传递指针

    例一:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    int main() {
    	int fd_pipe[2];
    	pid_t pid = fork();
    	if (pid < 0) {
    		perror("fork");
    	} 
    	if (pid == 0) {
    		sleep(2);
            char str[] = "1234455";
    	    write(fd_pipe[1], str, strlen(str));
    	    free(str);
    		_exit(0);
    	} else {
    		fcntl(fd_pipe[0], F_SETFL, 0);
    		while(1) {
    			sleep(4);
    			char *buf;
    			memset(buf, 0, sizeof(buf));
                read(fd_pipe[0], buf, sizeof(buf));
    			printf("buf is %s", buf);
    		}
    	}
    
    	return 0;
    }
    

     例二:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    void *thread_func(void *buff)
    {
        sleep(1);
        char *buffer = (char *)buff;
        buffer[22] = 'a';
        return NULL;
    }
    
    int main()
    {
        char *buffer = malloc(20);
        pthread_t th;
        pthread_create(&th, NULL, thread_func, buffer);
        pthread_join(th, NULL);
        return 0;
    }

    2.7 某些有特殊要求的系统调用

           例如epool_wait,正常情况下使用close关闭一个套接字后,epool会不再返回这个socket上的事件,但是如果你使用dup或dup2操作,将导致epool无法进行移除操作,此时再进行读写操作肯定是段错误的。

     

    更多相关内容
  • PTA——段错误

    万次阅读 多人点赞 2018-11-24 18:12:39
    在PTA上做题,做出来个段错误,一查吓一跳,发篇博客冷静下。(#^.^#) 1076 Wifi密码 (15 分) 下面是微博上流传的一张照片:“各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi ...

    在PTA上做题,做出来个段错误,一查吓一跳,发篇博客冷静下。(#^.^#)

    1076 Wifi密码 (15 分)

    下面是微博上流传的一张照片:“各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi 密码设置为下列数学题答案:A-1;B-2;C-3;D-4;请同学们自己作答,每两日一换。谢谢合作!!~”—— 老师们为了促进学生学习也是拼了…… 本题就要求你写程序把一系列题目的答案按照卷子上给出的对应关系翻译成 wifi 的密码。这里简单假设每道选择题都有 4 个选项,有且只有 1 个正确答案。

    //我的代码
    #include <stdio.h>
    #define MAX 101
    #define SIZE 5
    int main()
    {
    	int N = 0;
    	char str[MAX][SIZE];
    	scanf("%d", &N);
    	if (N > 100 || N < 0)
    		return 0;
    	for (int i = 0; i < 4 * N; i++)
    	{
    		scanf("%s", str[i]);
    		if (str[i][2] == 'T')
    		{
    			if (str[i][0] == 'A')
    				putchar('1');
    			else if (str[i][0] == 'B')
    				putchar('2');
    			else if (str[i][0] == 'C')
    				putchar('3');
    			else if (str[i][0] == 'D')
    				putchar('4');
    		}
    	}
    	return 0;
    }

    • 修改后的正确代码:将MAX扩大为原来的5倍,段错误是由于N最大可取到100,而MAX没有满足MAX=4*N的条件所导致 
    #include <stdio.h>
    #define MAX 501
    #define SIZE 5
    int main()
    {
    	int N = 0;
    	char str[MAX][SIZE];
    	scanf("%d", &N);
    	if (N > 100 || N < 0)
    		return 0;
    	for (int i = 0; i < 4 * N; i++)
    	{
    		scanf("%s", str[i]);
    		if (str[i][2] == 'T')
    		{
    			if (str[i][0] == 'A')
    				putchar('1');
    			else if (str[i][0] == 'B')
    				putchar('2');
    			else if (str[i][0] == 'C')
    				putchar('3');
    			else if (str[i][0] == 'D')
    				putchar('4');
    		}
    	}
    	return 0;
    }

     

    一、什么是段错误?

    一旦一个程序发生了越界访问,cpu 就会产生相应的保护,于是 segmentation fault 就出现了,通过上面的解释,段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的,还有可能是缺少文件或者文件损坏。

    二、段错误产生的原因

    • 下面是一些典型的段错误的原因:
    1. 非关联化空指针——这是特殊情况由内存管理硬件
    2. 试图访问一个不存在的内存地址(在进程的地址空间)
    3. 试图访问内存的程序没有权利(如内核结构流程上下文)
    4. 试图写入只读存储器(如代码段)

    1、访问不存在的内存地址

    在C代码,分割错误通常发生由于指针的错误使用,特别是在C动态内存分配。非关联化一个空指针总是导致段错误,但野指针和悬空指针指向的内存,可能会或可能不会存在,而且可能或不可能是可读的还是可写的,因此会导致瞬态错误。

    #include <stdio.h>  
      
    int main (void)  
    {  
        int *ptr = NULL;  
        *ptr = 0;  
        return 0;  
    } 

    现在,非关联化这些变量可能导致段错误:非关联化空指针通常会导致段错误,阅读时从野指针可能导致随机数据但没有段错误,和阅读从悬空指针可能导致有效数据,然后随机数据覆盖。

    2、访问系统保护的内存地址 

    #include <stdio.h>  
      
    int main (void)  
    {  
        int *ptr = (int *)0;  
        *ptr = 100;  
        return 0;  
    } 

    3、访问只读的内存地址

    写入只读存储器提出了一个 segmentation fault,这个发生在程序写入自己的一部分代码段或者是只读的数据段,这些都是由操作系统加载到只读存储器。

    #include <stdio.h>  
    #include <string.h>  
      
    int main (void)  
    {  
        char *ptr = "test";  
        strcpy (ptr, "TEST");  
        return 0;  
    }  
    
    #include <stdio.h>  
      
    int main (void)  
    {  
        char *ptr = "hello";  
        *ptr = 'H';  
        return 0;  
    }  
    输出结果:  
    段错误(核心已转储)  

    上述例子ANSI C代码通常会导致段错误和内存保护平台。它试图修改一个字符串文字,这是根据ANSI C标准未定义的行为。大多数编译器在编译时不会抓,而是编译这个可执行代码,将崩溃。

    包含这个代码被编译程序时,字符串“hello”位于rodata部分程序的可执行文件的只读部分数据段。当加载时,操作系统与其他字符串和地方常数只读段的内存中的数据。当执行时,一个变量 ptr 设置为指向字符串的位置,并试图编写一个H字符通过变量进入内存,导致段错误。编译程序的编译器不检查作业的只读的位置在编译时,和运行类unix操作系统产生以下运行时发生 segmentation fault。

    可以纠正这个代码使用一个数组而不是一个字符指针,这个栈上分配内存并初始化字符串的值:

    #include <stdio.h>  
      
    int main (void)  
    {  
        char ptr[] = "hello";  
        ptr[0] = 'H';  
        return 0;  
    }  

    即使不能修改字符串(相反,这在C标准未定义行为),在C char *类型,所以没有隐式转换原始代码,在c++的 const char *类型,因此有一个隐式转换,所以编译器通常会抓住这个特定的错误。

    4、空指针废弃

    因为是一个很常见的程序错误空指针废弃(读或写在一个空指针,用于C的意思是“没有对象指针”作为一个错误指示器),大多数操作系统内存访问空指针的地址,这样它会导致段错误。

    #include <stdio.h>  
      
    int main (void)  
    {  
        int *ptr = NULL;  
        printf ("%d\n", *ptr);  
        return 0;  
    } 

    这个示例代码创建了一个空指针,然后试图访问它的值(读值)。在运行时在许多操作系统中,这样做会导致段错误。

    非关联化一个空指针,然后分配(写一个值到一个不存在的目标)也通常会导致段错误。

    #include <stdio.h>  
      
    int main (void)  
    {  
        int *ptr = NULL;  
        *ptr = 1;  
        return 0;  
    }  

    下面的代码包含一个空指针,但当编译通常不会导致段错误,值是未使用的。因此,废弃通常会被优化掉,死代码消除。

    #include <stdio.h>  
      
    int main (void)  
    {  
        int *ptr = NULL;  
        *ptr;  
        return 0;  
    }  
    

    还有,比如malloc 动态分配内存,释放、置空完成后,不可再使用该指针。

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <string.h>  
      
    int main()  
    {  
        char* str=(char* )malloc(100);  
        if(*str)  
        {  
            return;   
        }  
        strcpy(str,"hello");  
        printf("%s\n",str);  
        free(str);  
        str=NULL;  
        strcpy(str,"abcdef");  
        return 0;  
    }  
    输出结果:  
    hello  
    段错误 (核心已转储)

    5、堆栈溢出

    #include <stdio.h>  
    #include <string.h>  
      
    int main (void)  
    {  
        main ();  
        return 0;  
    }  
    输出结果:  
    段错误(核心已转储)  

    上述例子的无限递归,导致的堆栈溢出会导致段错误,但无线递归未必导致堆栈溢出,优化执行的编译器和代码的确切结构。在这种情况下,遥不可及的代码(返回语句)行为是未定义的。因此,编译器可以消除它,使用尾部调用优化,可能导致没有堆栈使用。其他优化可能包括将递归转换成迭代,给出例子的结构功能永远会导致程序运行,虽然可能不是其他堆栈溢出。

    6、内存越界(数组越界,变量类型不一致等)

    #include <stdio.h>  
      
    int main (void)  
    {  
        char test[10];  
        printf ("%c\n", test[100000]);  
        return 0;  
    }  
    输出结果:  
    段错误(核心已转储)  
    

    三、一些注意事项

    1. 出现段错误时,首先应该想到段错误的定义,从它出发考虑引发错误的原因。
    2. 在使用指针时,定义了指针后记得初始化指针,在使用的时候记得判断是否为 NULL
    3. 在使用数组时,注意数组是否被初始化,数组下标是否越界,数组元素是否存在等
    4. 在访问变量,注意变量所占地址空间是否已经被程序释放掉
    5. 在处理变量时,注意变量的格式控制是否合理等

    参看:C语言再学习 -- GCC编译过程
    1.段错误的定义
    Ansers.com
    http://www.answers.com
    Definition of "Segmentation fault"
    http://www.faqs.org/qa/qa-673.html
    2.《什么是段错误》
    http://www.linux999.org/html_sql/3/132559.htm
    3.《Segment fault 之永远的痛》
    http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=program&Number=193239&page=2&view=collapsed&sb=5&o=all&fpart=
    4.《段错误bug的调试》
    http://www.cublog.cn/u/5251/showart.php?id=173718

    展开全文
  • C 总线错误 (bus error) - 段错误 (segmentation fault) 两个常见的运行时错误: bus error (core dumped) - 总线错误 (信息已转储) segmentation fault (core dumped) - 段错误 (信息已转储) 错误信息对引起这两...

    C 总线错误 (bus error) - 段错误 (segmentation fault)

    两个常见的运行时错误

    • bus error (core dumped) - 总线错误 (信息已转储)
    • segmentation fault (core dumped) - 段错误 (信息已转储)

    错误信息对引起这两种错误的源代码错误并没有作简单的解释,上面的信息并未提供如何从代码中寻找错误的线索,而且两者之间的区别也并不是十分清楚,时至今日依然如此。

    错误就是操作系统所检测到的异常,而这个异常是尽可能地以操作系统方便的原则来报告的。总线错误和段错误的准确原因在不同的操作系统版本上各不相同。这里所描述是运行于 SPARC 架构的 SunOS 出现的这两类错误以及产生错误的原因。

    当硬件告诉操作系统一个有问题的内存引用时,就会出现这两种错误。操作系统通过向出错的进程发送一个信号与之交流。信号就是一种事件通知或一个软件中断,在 UNIX 系统编程中使用很广,但在应用程序编程中几乎不使用。在缺省情况下,进程在收到总线错误段错误信号后将进行信息转储并终止。不过可以为这些信号设置一个信号处理程序 (signal handler),用于修改进程的缺省反应。

    信号是由于硬件中断而产生的。对中断的编程是非常困难的,因为它们是异步发生的 (其发生时间是不可预测的)。阅读信号的主文档和头文件 usr/include/sys/signal.h

    1. 在 PC 上捕捉信号

    信号处理函数是 ANSI C 的一部分,与 UNIX 一样,它也同样适用于 PC。例如 PC 程序员可以使用 signal() 函数来捕捉 Ctrl-Break 信号,防止用户用这种方法中断程序。

    在任何使用信号的源文件中,都必须在文件前面增加一行 #include <singal.h>

    这条信息的 core dumped 部分来源于很早的过去,那时所有的内存都是由铁氧化物圆环 (也就是 core,指磁心) 制造的。半导体成为内存的主要制造材料的时间已经超过十五年,但 core 这个词仍然被用作内存的同义词。

    core [kɔː(r)]:n. 核心,要点,果心,磁心 vt. 挖...的核
    

    2. 总线错误 (bus error)

    事实上,总线错误几乎都是由于未对齐的读或写引起的。它之所以称为总线错误,是因为出现未对齐的内存访问请求时,被堵塞的组件就是地址总线。对齐 (alignment) 的意思就是数据项只能存储在地址是数据项大小的整数倍的内存位置上。在现代的计算机架构中,尤其是 RISC 架构,都需要数据对齐,因为与任意的对齐有关的额外逻辑会使整个内存系统吏大且更慢。通过迫使每个内存访问局限在一个 Cache 行或一个单独的页面内,可以极大地简化 (并加速) 如 Cache 控制器和内存管理单元这样的硬件。

    我们表达数据项不能跨越页面或 Cache 边界规则的方法多少有些问接,因为我们用地址对齐这个术语来陈述这个问题,而不是直截了当说是禁止内存跨页访问,但它们说的是同一回事。例如,访问一个 8 字节的 double 数据时,地址只允许是 8 的整数倍。所以一个 double 数据可以存储于地址 24、8008 或 32768,但不能存储于地址 1006 (因为它无法被 8 整除)。页和 Cache 的大小是经过精心设计的,这样只要遵守对齐规则就可以保证一个原子数据项不会跨越一个页或 Cache 块的边界。

    2.1 引起总线错误的程序

    //============================================================================
    // Name        : main
    // Author      : Yongqiang Cheng
    // Version     : Version 1.0.0
    // Copyright   : Copyright (c) 2019 Yongqiang Cheng
    // Description : Hello World in C++, Ansi-style
    //============================================================================
    
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
    	union union_name
    	{
    		char a[10];
    		int i;
    	} union_object;
    
    	printf("argc = %d\n", argc);
    
    	for (int idx = 0; idx < argc; ++idx)
    	{
    		printf("argv[%d] --> %s\n", idx, argv[idx]);
    	}
    
    	printf("argv[argc] = %p\n\n", (void*)argv[argc]);
    
    	int *pt = (int *)&(union_object.a[1]);
    	int *pi = (int *)&(union_object.i);
    
    	*pt = 17;
    
    	printf("*pt = %d\n", *pt);
    	printf("pt = %p\n", pt);
    	printf("pi = %p\n", pi);
    
    	return 0;
    }
    
    argc = 1
    argv[0] --> D:\visual_studio_workspace\yongqiang\Debug\yongqiang.exe
    argv[argc] = 00000000
    
    *pt = 17
    pt = 008FFA39
    pi = 008FFA38
    请按任意键继续. . .
    

    在这里插入图片描述

    pt 中未对齐的地址会引起一个总线错误!
    在实际的运行中并没有出现错误,运行环境如下:
    在这里插入图片描述

    x86 体系结构,如果没有默认对齐的话,访问速度会降低。读一个 int 本来只读一次的,没有对齐的话要读 2 次才行,把第一次的尾巴和第二次的头拼起来。如果在代码中将对齐检查功能打开,运行后能显示 bus error

    这可能导致一个总线错误,因为数组和 int 的联合确保数组 a 是按照 int 的 4 字节对齐的,a+l 的地址肯定未按 int 对齐。我们试图往这个地址存储 4 个字节的数据,但这个访问只是按照单字节的 char 对齐,这就违反了规则。一个好的编译器发现不对齐的情况时会发出警告,但它并不能检测到所有不对齐的情况。

    编译器通过自动分配和填充数据 (在内存中) 来进行对齐。当然,在磁盘或磁带上并没有这样的对齐要求,所以程序员对它们可以很偷快地不必关心数据对齐。但是,当他们把一个 char 指针转换为 int 指针时,就会出现神秘的总线错误。几年前,当检测到一个内存奇偶检验错误时也会产生总线错误。现在,内存芯片已经非常可靠,而且很好地得到了错误检测和修正电路的保护,所以在应用程序编程这一级,奇偶检验错误几乎不再听闻。总线错误也可能由于引用一块物理上不存在的内存引起。如果不遭遇一个淘气的驱动程序,你恐怕不大可能遭遇这种不幸。

    //============================================================================
    // Name        : main
    // Author      : Yongqiang Cheng
    // Version     : Version 1.0.0
    // Copyright   : Copyright (c) 2019 Yongqiang Cheng
    // Description : Hello World in C++, Ansi-style
    //============================================================================
    
    #include <stdio.h>
    
    int main(int argc, char **argv)
    {
    #if defined(__GNUC__)
    #if defined(__i386__)
    	/* Enable Alignment Checking on x86 */
    	__asm__("pushf\norl $0x40000,(%esp)\npopf");
    #elif defined(__x86_64__)
    	/* Enable Alignment Checking on x86_64 */
    	__asm__("pushf\norl $0x40000,(%rsp)\npopf");
    #endif
    #endif
    
    	union
    	{
    		char a[10];
    		int i;
    	} u;
    
    	int *p = (int*) &(u.a[1]);
    	*p = 17;
    
    	return 0;
    }
    
    
    16:26:55 **** Build of configuration Debug for project yongqiang_example ****
    make all 
    Building file: ../src/yongqiang.c
    Invoking: GCC C Compiler
    gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/yongqiang.d" -MT"src/yongqiang.o" -o "src/yongqiang.o" "../src/yongqiang.c"
    Finished building: ../src/yongqiang.c
     
    Building target: yongqiang_example
    Invoking: GCC C Linker
    gcc  -o "yongqiang_example"  ./src/yongqiang.o   
    Finished building target: yongqiang_example
     
    
    16:26:56 Build Finished (took 524ms)
    
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ pwd
    /home/strong/eclipse-work/yongqiang_example/Debug
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ ll
    total 56
    drwxrwxr-x 3 strong strong  4096 Mar 30 16:26 ./
    drwxrwxr-x 5 strong strong  4096 Mar 28  2018 ../
    -rw-rw-r-- 1 strong strong  1009 Mar 30 16:26 makefile
    -rw-rw-r-- 1 strong strong   231 Mar 30 16:26 objects.mk
    -rw-rw-r-- 1 strong strong   392 Mar 30 16:26 sources.mk
    drwxrwxr-x 2 strong strong  4096 Mar 30 16:26 src/
    -rwxrwxr-x 1 strong strong 29720 Mar 30 16:26 yongqiang_example*
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ 
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ ./yongqiang_example 
    Bus error (core dumped)
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$
    

    3. 段错误 (segmentation fault)

    段错误或段违规 (segmentation violation)。在 Sun 的硬件中,段错误是由于内存管理单元 (负责支持虚拟内存的硬件) 的异常所致,而该异常则通常是由于解除引用一个未初始化或非法值的指针引起的。如果指针引用一个并不位于你的地址空间中的地址,操作系统便会对此进行干涉。

    一个小型的会引起段错误的程序如下:

    	int *pt = 0;
    	*pt = 17;
    
    //============================================================================
    // Name        : main
    // Author      : Yongqiang Cheng
    // Version     : Version 1.0.0
    // Copyright   : Copyright (c) 2019 Yongqiang Cheng
    // Description : Hello World in C++, Ansi-style
    //============================================================================
    
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
    	printf("argc = %d\n", argc);
    
    	for (int idx = 0; idx < argc; ++idx)
    	{
    		printf("argv[%d] --> %s\n", idx, argv[idx]);
    	}
    
    	printf("argv[argc] = %p\n\n", (void*)argv[argc]);
    
    	int *pt = 0;
    	*pt = 17;
    
    	printf("*pt = %d\n", *pt);
    	printf("pt = %p\n", pt);
    
    	return 0;
    }
    
    
    16:31:07 **** Build of configuration Debug for project yongqiang_example ****
    make all 
    Building file: ../src/yongqiang.c
    Invoking: GCC C Compiler
    gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/yongqiang.d" -MT"src/yongqiang.o" -o "src/yongqiang.o" "../src/yongqiang.c"
    Finished building: ../src/yongqiang.c
     
    Building target: yongqiang_example
    Invoking: GCC C Linker
    gcc  -o "yongqiang_example"  ./src/yongqiang.o   
    Finished building target: yongqiang_example
     
    
    16:31:08 Build Finished (took 571ms)
    
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ pwd
    /home/strong/eclipse-work/yongqiang_example/Debug
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ 
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ ll
    total 56
    drwxrwxr-x 3 strong strong  4096 Mar 30 16:31 ./
    drwxrwxr-x 5 strong strong  4096 Mar 28  2018 ../
    -rw-rw-r-- 1 strong strong  1009 Mar 30 16:31 makefile
    -rw-rw-r-- 1 strong strong   231 Mar 30 16:31 objects.mk
    -rw-rw-r-- 1 strong strong   392 Mar 30 16:31 sources.mk
    drwxrwxr-x 2 strong strong  4096 Mar 30 16:31 src/
    -rwxrwxr-x 1 strong strong 29680 Mar 30 16:31 yongqiang_example*
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ 
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$ ./yongqiang_example 
    argc = 1
    argv[0] --> ./yongqiang_example
    argv[argc] = (nil)
    
    Segmentation fault (core dumped)
    strong@foreverstrong:~/eclipse-work/yongqiang_example/Debug$
    

    一个微妙之处是导致指针具有非法的值通常是由于不同的编程错误所引起的。和总线错误不同,段错误更像是一个间接的症状而不是引起错误的原因。

    一个更糟糕的微妙之处是,如果未初始化的指针恰好具有未对齐的值 (对于指针所要访问的数据而言),它将会产生总线错误,而不是段错误。对于绝大多数架构的计算机而言确实如此,因为 CPU 先看到地址,然后再把它发送给 MMU。

    violation [ˌvaɪəˈleɪʃn]:n. 违反,妨碍,侵害,违背,强奸
    

    在你的代码中,对非法指针值的解除引用操作可能会像上面这样显式地出现,也可能在库函数中出现 (传递给它一个非法值)。令人不快的是,你的程序如果进行了修改 (如在调试状态下编译或增加额外的调试语句),内存的内容便很容易改变,于是这个问题被转移到别处或干脆消失。段错误是非常难于解决的,而且只有非常顽固的段错误才会一直存在。当你看到同事们神色严峻地带着逻辑分析器和示波器进入测试实验室时,便知道他们肯定遇到了真正的麻烦。

    通常导致段错误的几个直接原因:

    • 解除引用一个包含非法值的指针。
    • 解除引用一个空指针 (常常由于从系统程序中返回空指针,并未经检查就使用)。
    • 在未得到正确的权限时进行访问,例如,试图往一个只读的文本段存储值就会引起段错误。
    • 用完了堆栈或堆空间 (虚拟内存虽然巨大但绝非无限)。

    下面这个说法可能过于简单,但在绝大多数架构的绝大多数情况下,总线错误意味着 CPU 对进程引用内存的一些做法不满,而段错误则是 MMU 对进程引用内存的一些情况发出抱怨。

    以发生频率为序,最终可能导致段错误的常见编程错误是:

    1. 坏指针值错误:在指针赋值之前就用它来引用内存,或者向库函数传送一个坏指针 (不要上当!如果调试器显示系统程序中出现了段错误,并不是因为系统程序引起了段错误,问题很可能还存在于自己的代码中)。第三种可能导致坏指针的原因是对指针进行释放之后再访问它的内容。可以修改 free 语句,在指针释放之后再将它置为空值。
      free(p); p = NULL;

    这样,如果在指针释放之后继续使用该指针,至少程序能在终止之前进行信息转储。

    1. 改写 (overwrite) 错误:越过数组边界写入数据,在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构 (在动态分配的内存之前的区域写入数据就很容易发生这种情况)。
      p = malloc(256); p[-1] = 0; p[256] = 0;

    2. 指针释放引起的错误:释放同一个内存块两次,或释放一块未曾使用 malloc 分配的内存,或释放仍在使用中的内存,或释放一个无效的指针。一个极为常见的与释放内存有关的错误就是在 for(p = start; p; p = p-> next) 这样的循环中迭代一个链表,并在循环体内使用 free(p) 语句。这样,在下一次循环迭代时,程序就会对己经释放的指针进行解除引用操作,从而导致不可预料的结果。

    //============================================================================
    // Name        : main
    // Author      : Yongqiang Cheng
    // Version     : Version 1.0.0
    // Copyright   : Copyright (c) 2019 Yongqiang Cheng
    // Description : Hello World in C++, Ansi-style
    //============================================================================
    
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
    	printf("argc = %d\n", argc);
    
    	for (int idx = 0; idx < argc; ++idx)
    	{
    		printf("argv[%d] --> %s\n", idx, argv[idx]);
    	}
    
    	printf("argv[argc] = %p\n\n", (void*) argv[argc]);
    
    	int *pt = NULL;
    
    	free(pt);
    
    	printf("pt = %p\n", pt);
    
    	return 0;
    }
    
    
    argc = 1
    argv[0] --> /home/strong/eclipse-work/yongqiang_example/Debug/yongqiang_example
    argv[argc] = (nil)
    
    pt = (nil)
    
    

    在遍历链表时正确释放元素的方法是使用临时变量存储下一个元素的地址。这样就可以安全地在任何时候释放当前元素,不必担心在取下一个元素的地址时还要引用它,代码如下:

    	struct node *p, *start, *tmp;
    	for (p = start; p; p = tmp)
    	{
    		tmp = p->next;
    		free(p);
    	}
    

    3.1 程序空间是否足够?

    如果你的程序所需的内存超过了操作系统所能提供给它的数量,程序就会发出一条段错误信息并终止。可以用一种简单的方法把这种段错误与其他基于 Bug 的段错误区分开来。

    要弄清程序是否用完了堆栈,可以在 dbx 命令下运行该程序:

    % dbx a.out
    (dbx) catch SIGSEGV
    
    (dbx) run
    ...
    signal SEGV (segmentation violation) in <some_routine> at 0xeff57708
    (dbx) where
    
    如果现在可以看到调用链,那说明堆栈空间还没有用完。
    但是,如果看到像下面这样的东西:
    
    fetch at 0xeffe7a60 failed -- I/O error
    (dbx)
    

    那么,堆找很可能已经用完。上面这个十六进制数就是可以提取或映射的堆栈地址。
    你也可以尝试在 C-shell 中调整堆栈段的大小限制。

    limit stacksize 10
    

    你可以在 C-shell 中调整堆栈段和数据段的最大值。进程的总地址空间仍然受交换区大小的限制,可以用 swap -s 命令查看交换区的大小。

    当程序出现坏指针值时,什么样的结果都有可能发生。一种广被接受的说法是,如果你走运,指针将指向你的地址空间之外,这样第一次使用该指针时就会使程序进行信息转储后终止。如果你不走运,指针将指向你的地址空间之内,并损坏 (改写) 所指向的内存的任何信息。这将引起隐晦的 Bug,非常难以捕捉。

    展开全文
  • ubuntu 20.04出现段错误核心已转储,使用GDB测试找不到core文件解决方案

           作为一个半路出家的linuc用户,coredump这个问题太让人抓狂了,网上找了好多都是不全面,不适应或者看不懂;现在终于解决了,记录一下防止以后出现还是无解,同时也分享给大家,希望大家能少踩一些坑。

    目录

    1.什么是段错误

    2. 解决方案

    3.解决过程

    3.1 生成Core文件

    3.1.1 使用ulimit -a命令查看core文件大小限制

    3.1.2 在终端输入 cat /proc/sys/kernel/core_pattern 查看core的生成路径。

     3.1.3 修改core文件生成路径

    3.2 GDB测试

    3.2.1 启动gdb

    3.2.2 输入bt回溯定位

    参考资料:


    1.什么是段错误

            core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIGSEGV信号,然后就会core dump)。产生段错误的原因大致上有三类:访问不存在的内存地址、访问系统保护的内存地址和访问只读的内存地址

    2. 解决方案

    网上的资料虽然比较乱,但是也提供了一个解决问题的思路:

    (1)设置core文件,找到段错误生成的core文件

    (2)利用core文件,使用GDB测试找到问题所在

    3.解决过程

    先看问题:

    3.1 生成Core文件

    3.1.1 使用ulimit -a命令查看core文件大小限制

    可以看到core file size的大小为0,文件根本装不进,需要使用 ulimit -c unlimited 修改这个文件的大小

     修改成功后,按照网上的说法,再运行程序就会生成core文件,一般路径和可执行程序一个路径。但是在ubuntu20.04下,怎么也找不到去哪里了(反正我的是这样),因此需要查看core文件的生成路径。

    3.1.2 在终端输入 cat /proc/sys/kernel/core_pattern 查看core的生成路径。

     转到这个路径下去找是找不到core文件,这是因为ubuntu的服务apport.service。自动生成崩溃报告,官方为了自动收集错误的。我们肯定想到修改路径的办法,那就演示一下会怎么样。

    core的设置主要有两个命令:

     //控制core文件的文件名中是否添加pid作为扩展
    echo "1" > /proc/sys/kernel/core_uses_pid  
    //设置core文件的输出路径和输出文件名,这里我的路径是/home/boy/corefile,文件名就是后面的部分
    echo "/home/boy/corefile/core-%e-%p-%t"> /proc/sys/kernel/core_pattern 
    
    //参数说明
    %p - insert pid into filename 添加pid
    %u - insert current uid into filename 添加当前uid
    %g - insert current gid into filename 添加当前gid
    %s - insert signal that caused the coredump into the filename 添加导致产生core的信号
    %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
    %h - insert hostname where the coredump happened into filename 添加主机名
    %e - insert coredumping executable name into filename 添加程序名

    我直接用echo "/home/boy/corefile/core-%e-%p-%t"> /proc/sys/kernel/core_pattern 进行修改,结果如图

     3.1.3 修改core文件生成路径

    因为我们修改的core_pattern文件是只读文件,没法这样修改。所以要换一种思路,修改不了就先停掉apport.service,这个服务对我们来说基本没用。

    错误报告的部分操作命令如下:

    //1.启用错误报告
    sudo systemctl enable apport.service
    //或
    sudo service apport start
    
    //2.关闭错误报告
    sudo systemctl disable apport.service
    //或
    sudo service apport stop

    所以,用sudo service apport stop关闭错误报告后我们再看core文件的路径会怎么样

     可以看到,路径发生了变化,再运行一次试试,看现在能不能生成core

     可以看到,运行完后用ll查看生成了core文件,方法有限,下面就是GDB调试找到错误的位置了。

    3.2 GDB测试

    GDB详细说明请看参考资料大佬的整理,这里只记录一下我怎么测试的

    3.2.1 启动gdb

    输入gdb 运行文件  core文件,例如:

    gdb  bin/run_vo  core

    结果如下:

     可以看到对内存出现非法访问时将收到段错误信号SIGSEGV下面就是出错的位置,我们还可以使用backtrace回溯定位问题。

    3.2.2 输入bt回溯定位

     可以看到现在的报告更加详细。

    到此,coredump问题已经解决,输入q,即可退出gdb,剩下就是修改问题部分了。

    参考资料:

    (69条消息) ubuntu20.04 如何生成core文件_Jqivin的博客-CSDN博客icon-default.png?t=M276https://blog.csdn.net/Jqivin/article/details/121908435?ops_request_misc=&request_id=&biz_id=102&utm_term=ubuntu20.04%E6%89%BE%E4%B8%8D%E5%88%B0core%E6%96%87%E4%BB%B6&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-4-121908435.142^v5^article_score_rank&spm=1018.2226.3001.4187(69条消息) Ubuntu18.04 产生不了core文件之解决办法_qq76211822的博客-CSDN博客_/usr/share/apport/apporticon-default.png?t=M276https://blog.csdn.net/sz76211822/article/details/112181664?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164879853216782248562235%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164879853216782248562235&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-112181664.142^v5^article_score_rank&utm_term=%E4%BF%AE%E6%94%B9%2Fetc%2Fdefault%2Fapport&spm=1018.2226.3001.4187(69条消息) linux下gdb调试方法与技巧整理_花开蝶自来-liu的博客-CSDN博客_gdb调试icon-default.png?t=M276https://blog.csdn.net/niyaozuozuihao/article/details/91802994 (69条消息) c++如何解决段错误 (核心已转储)_肥鼠路易的博客-CSDN博客_核心已转储icon-default.png?t=M276https://blog.csdn.net/weixin_44991673/article/details/118030855

    展开全文
  • qt 段错误 解决方案

    热门讨论 2010-04-21 20:37:49
    移植qt应用程序时 会出现段错误 主要介绍怎么解决该错误
  • 在Linux上如何得到一个段错误的核心转储 文章目录什么是段错误?步骤1:运行 valgrind如何获得一个核心转储ulimit:设置核心转储的最大尺寸kernel.core_pattern:核心转储保存在哪里kernel.core_pattern 和 Ubuntu...
  • 段错误产生原因及调试总结

    千次阅读 2019-03-13 09:51:00
    一、段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等等情况。 二、段错误产生的原因 1、...
  • gdb调试段错误

    万次阅读 2020-07-08 22:10:27
    前言 使用gdb来调试程序应该算是Linux开发的必备技能吧,今天来看看如何使用gdb调试段错误(Segmentation fault)。 如何...
  • c++如何解决段错误 (核心已转储)

    千次阅读 2021-06-18 17:35:26
    在后端优化中遇到了段错误 (核心已转储)问题,寻求解决方法。 首先通过不断的注释部分代码,运行,注释,运行,找到一个大概的范围是错在哪一块。 这类问题只能说根本原因是内存的非法操作。建议生成内存段错误转储...
  • 常见段错误原因总结

    千次阅读 多人点赞 2020-04-06 23:16:58
    常见段错误原因总结前言原因汇总一、段错误究根到底就是访问了非法内存:二、数组越界三、scanf错误使用:四、指针访问只读内存区: 前言 之前写代码的时候总是会出现段错误:segment fault。所以特地来总结一下常见...
  • 段错误总结

    万次阅读 多人点赞 2017-04-10 20:13:36
    最近试着写了华为编程大赛的程序,由于C++下编程还不熟练,出现较多的一个问题是段错误,结合网上资料总结下。 http://www.cnblogs.com/lidabo/p/4545625.html 段错误是什么 一句话来说,段错误是指访问的内存超出...
  • 段错误(核心已转储)的问题原因

    千次阅读 2022-02-14 22:43:54
    可执行文件是分段存储的,加载进内存也是分段的,如代码段、数据段、堆、栈等,段错误的原因往往是碰到了不该碰到的内存位置(如系统保留段、代码段不能被修改,使用空指针等)。 核心已转储就是说进程结束之前,...
  • 在Linux服务器上训练模型时,每当训练数据时程序就会自动停掉,报段错误(核心已转储),经过搜索有人说是程序越界了,stack太小了,于是我用ulimit -a 查看了Linux中stack的大小,然后又使用ulimit -c 10240修改...
  • 段错误(核心已转储)(core dumped)问题的分析方法1.段错误1.1段错误的含义1.2实际常见原因2.生成core文件2.1.使用命令 ulimit -a 查看core文件大小限制2.2使用 命令 ulimit -c 10240 (10240的位置可以是任意一个...
  • 段错误的分析方法: 段错误一般借助于可调试(使用-g选项进行编译)的原程序和核心转储后的core file来进行分析,如针对我写的程序,其步骤为: gcc –g –o typedef test_typedef.c (生成可调试的可执行程序) ./...
  • 如何解决段错误

    万次阅读 多人点赞 2017-12-03 14:55:03
    程序运行时崩溃应该是经常遇到的事情,本篇文中就来讲解一下在Linux下如何解决程序崩溃,也就是发生了段错误之后找到程序具体出错的地方。如果程序通过了编译,但是在使用命令 ./ a.out 运行时程序崩溃了,即显示 ...
  • Linux环境下段错误的产生原因及调试方法小结

    万次阅读 多人点赞 2018-07-06 11:12:26
    http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且...借此机会系统学习了一下,这里对Linux环境下的段错误做个小...
  • !... 如图所示 7-5 求整数序列中出现次数最多的数 (15 分) 本题要求统计一个整型序列中出现次数最多的整数及其出现...数组已经定得足够大了,codeblocks运行没有任何问题,pta却显示段错误? 请各位大佬指点一下。
  • 段错误(核心已转储)-linux

    万次阅读 2019-02-23 10:19:03
    在终端输入:ulimit -a 会发现提示很多 重点关注两个,一个是core file size,还有一个是stack size vi .bashrc 在bashrc文件最后加 ulimit -c unlimited ulimit -s 819200 保存后关闭终端 ... ...
  • 但因为各个库之间的版本迭代,易产生BUG,如我在Linux系统下使用Matplotlib库,就会出现段错误(核心已转储)的错误提示,导致无法进行正常的学习工作。据查阅资料,使用Python3.6.5版本较为稳定,故基于此,安装...
  • C语言再学习 -- 段错误(核心已转储)

    万次阅读 多人点赞 2016-12-21 12:22:57
    参看:Linux下的段错误产生的原因及调试方法参看:Linux环境下段错误的产生原因及调试方法小结参看:维基百科--Segmentation fault参看:LINUX内核段错误调试详细指南精品培训PPT讲义一、什么是段错误?一旦一个程序...
  • ROS/c++常见段错误以及排查

    千次阅读 2022-04-05 11:19:15
    在C++编程中,我们经常会发现段错误这类问题,而这类问题经常是指访问的内存超出了系统所给这个程序的内存空间。一般是随意使用野指针或者数组、数组越界等原因造成的。段错误是指访问的内存超出了系统给这个程序所...
  • 段错误?打的就是段错误!!

    万次阅读 多人点赞 2020-05-22 11:56:03
    文章目录①一段有段错误的代码②跟我一起 -> 直达病灶③看我对症下药 呵,段错误?自从我看了这篇文章,我还会怕你个小小段错误? 请打开你的Linux终端,跟紧咯,准备发车!!嘟嘟嘟哒~~ ①一段有段错误的代码 #...
  • 一、段错误的形式:  在编程中以下几类做法容易导致段错误,基本上是错误地使用指针引起的。 1)访问系统数据区,尤其是往系统保护的内存地址写数据最常见就是给一个指针以0地址。 2)内存越界(数组越界,变量类型...
  • PAT刷题之段错误

    千次阅读 2019-01-19 10:00:03
    PAT刷题之段错误【updating…】 1.什么是段错误? 先给出一张图,所谓的段错误就是:Segmentation fault 在提交 pat 的一道题时,出现了这个段错误。其实更具体的报错是: Program received signal SIGSEGV,...
  • 在做ORB_SLAM2的时候,编译已通过,运行时出错:段错误 (核心已转储),请问怎么解决? caomi@caomi-ubuntu:~/catkin_ws/src/ORB_SLAM2_modified$ rosrun ORB_SLAM2 RGBD Vocabulary/ORBvoc.txt Examples/RGB-D/TUM1....
  • 段错误(核心已转储)的原因

    万次阅读 多人点赞 2018-11-01 20:04:00
    一、什么是段错误? 一旦一个程序发生了越界访问,cpu 就会产生相应的保护,于是 segmentation fault 就出现了,通过上面的解释,段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统...
  • Qt 段错误解决办法1

    千次阅读 2018-04-11 11:37:51
    做多语言翻译,无缘无故段错误
  • PAT 段错误的原因和解决方法

    千次阅读 多人点赞 2019-08-04 13:59:05
    目录 一....查找的方法也比较简单,首先对可疑的代码注释掉,提交到OJ上试试,如果确实是⾃⼰注释掉的 这段代码发⽣了段错误,那么对应的OJ判题结果就会变为答案错误⽽不再是段错误,说明注释 掉...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,805,563
精华内容 722,225
关键字:

段错误

友情链接: jufang3.rar