精华内容
下载资源
问答
  • c 总线错误
    2020-01-01 17:59:52

    Error

    当硬件告诉操作系统一个有问题的内存引用时,就会出现这两种错误。操作系统通过向出错的进程发送一个信号与之交流。进程收到“bus error”或“segmentation fault”信号后将进行信息转储并终止。不过可以为这些信号设置一个信号处理程序(signal handler),用于修改进程的缺省反应。

    bus error (core dumped) 总线错误

    • CPU对进程引用内存的一些做法不满
    • 事实上,总线错误几乎都是由于未对齐的读或写引起的。之所以称为总线错误,是因为出现未对齐的内存访问的请求时,被堵塞的组件就是地址总线。
    • alignment(对齐)的意思就是:数据项只能存储在地址是数据项大小的整数倍的内存位置上。
    • 通过博士每个内存访问局限在一个Cache行货一个单独的页面内,可以极大的简化(并加速)如Cache控制器和内存管理单元这样的硬件。
    • 访问一个8字节的double数据时,地址只允许是8的整数倍。所以一个double数据可以存储于地址24、8008、32768,但不能存储于地址1006(因为它无法被8整除)。
    • example
    union{
    	char a[10];
    	int i;
    }u;
    int *p = (int *)&(u.a[1]);
    * p = 17;  /*p中未对齐的地址会引起一个总线错误!*/
    
    
    • 上述总线错误,因为数组和int的联合确保数组a是按照int的4字节对齐的,所以“a+1”的地址肯定未按int对齐。然后我们试图往这个地址存储4个字节的数据,但这个访问只是按照单字节的char对齐,这就违反了规则。
    • 总线错误也可能是由于引用一块物理上不存在的内存引起的。

    segmentation fault(core dumped)段错误

    MMU对进程引用内存的一些情况发出抱怨。
    几个直接的原因:

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

    以发生频率为序:

    1.坏指针值错误:

    1.1 在指针赋值之前就用它来引用内存
    1.2 向库函数传送一个坏指针
    1.3 对指针进行释放之后再访问它的内容,可以修改free语句,在指针释放后再将它置位空值。

    free(p);
    p = NULL;
    

    2.改写(overwrite)错误:

    2.1 越过数组边界写入数据
    2.2. 在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构

    p=malloc(256); p[-1]=0; p [256]= 0;
    

    3.指针释放引起的错误

    3.1 释放同一个内存块2次
    3.2 释放一块未曾使用malloc分配的内存
    3.3 释放仍在使用中的内存
    3.4 释放一个无效的指针

    //如何在链表中释放元素
    /*
    在遍历链表时正确释放元素的方法是使用临时变量存储下一个元素的地址,这样就可以安全地在任何时候释放当前元素,不必担心在取一下一个元素的地址时还要引用它。
    */
    struct node *p, *start, *tmp;
    for(p =start;p; p = tmp)
    {
    	tmp = p ->next;
    	free(p);
    }
    

    软件信条

    程序所用的内从超过了系统能提供的数量,这在任务堆栈中也适用(某个任务在创建时定义了任务的堆栈),程序就会发出一条“段错误”信息并终止。如何区别这种错误和其他基于Bug的段错误。即弄清楚程序是否已用完了堆栈,如何查看。

    更多相关内容
  • 当硬件告诉操作系统一个有问题的内存引用时,就会出现这两种错误。操作系统通过向出错的进程发送一个信号与之交流。进程收到 “bus error” 或 "segmentation fault"信号后...之所以称为总线错误,是因为出现未对齐的..
    • 当硬件告诉操作系统一个有问题的内存引用时,就会出现这两种错误。操作系统通过向出错的进程发送一个信号与之交流。进程收到 “bus error” 或 "segmentation fault"信号后将进行信息转储并终止。不过可以认为这些信号设置一个信号处理程序(signal handler),用于修改进程的缺省反应。

    • bus error (core dumped) 总线错误

      • CPU对进程引用内存的一些做法不满

      • 事实上,总线错误几乎都是由于未对齐的读或写引起的。之所以称为总线错误,是因为出现未对齐的内存访问的请求时,被堵塞的组件就是地址总线。

      • alignment(对齐)的意思就是:数据项只能存储在地址是数据项大小的整数倍的内存位置上。

      • 访问一个8字节的double数据时,地址只允许是8的整数倍。所以一个double数据可以存储于地址24、8008、32768,但不能存储于地址1006(因为它无法被8整除)。

      在这里插入图片描述

      • 上述总线错误,因为数组和int的联合确保数组a是按照int的4字节对齐的,所以“a+1”的地址肯定未按int对齐。然后我们试图往这个地址存储4个字节的数据,但这个访问只是按照单字节的char对齐,这就违反了规则

      • 上述的总线错误,a+1是char数组的第二个地址,是按照1个字节,所以你在这个1个字节的地址上要存储4个字节的int,未对齐,所以出现问题。

      • 总线错误也可能是由于引用一块物理上不存在的内存引起的。

    • segmentation fault (core dumped) 段错误

    • 直接原因

       解引用一个包含非法值的指针
       解引用一个空指针(从系统程序中返回空指针,并未经检查就开始使用)
       在未得到正确的权限时进行访问。例如:试图往一个只读的文本端存储值就会引起段错误。
       用完了堆栈或堆空间
      
    • 发生频率

       坏指针错误
           在指针赋值之前就用它来引用内存
           向库函数传送一个坏指针
           对指针进行释放之后再访问它的内容,可以修改free语句,在指针释放后再将它置位空值。
      

    在这里插入图片描述

        改写(overwrite)错误
            越过数组边界写入数据
            在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构
    

    在这里插入图片描述

        指针释放引起的错误
            释放同一个内存块2次
            释放一块未曾使用malloc分配的内存
            释放仍在使用中的内存
            释放一个无效的指针
    

    在这里插入图片描述

    • page fault 页错误

      • 定义

        • 当cpu执行进程的某个页面时,发现他要访问的页(虚拟地址的页)没有在物理内存中,而导致的中断(页错误)
      • 处理

        • 页错误发生后,操作系统去查询保存可执行文件和其进程虚拟空间映射关系的数据结构

        • 找到空页面所在的VMA虚拟内存区域。然后通过它VMA计算出空页面在可执行文件中偏移

        • 然后再物理内存中分配一个物理页面,并将该物理页和虚拟空间中虚拟页建立映射关系。

        • 最后将cpu控制权返还给进程,从刚才页错误的地方继续执行。

    • 虚拟内存区域(VMA: Virtual Memory Area):linux将进程虚拟空间中的一个段叫做虚拟内存区域。windows叫做虚拟段。

      在这里插入图片描述

    • 进程及页映射

       假设这个进程 add 是计算1+1 1+2 1+3 ……到 1+100000的值。
       由图可知,现在由于可执行文件到虚拟空间中只映射了VP0~VP7这8个页,并且其中的计算都在这些页中标识。
       并且VP0 VP1 VP7都已经映射到物理内存页的PP0 PP2 PP3处。
      
    • 映射关系和数据结构

       图中从左到右依次是:进程的可执行文件(存储在磁盘中) – 进程虚拟地址空间 – 物理内存地址空间
       **映射关系1(绿色)**:指的是可执行文件到虚拟地址空间的映射,其映射关系保持在操作系统数据结构1中。
       **映射关系2(紫色)**:指的是虚拟地址空间到物理内存页直接的映射,其映射关系保持在操作系统数据结构2中。
      
    • 页错误产生

       进程ADD执行1+1时,从操作系统保存的数据结构2中可以查到映射关系2,
       由于其进程虚拟空间页VP0已经映射到物理内存页PP0处,所以CPU执行,OK,结果是2。完成!
       进程ADD执行1+2时,从操作系统保存的数据结构2中可以查到映射关系2,
       由于其进程虚拟空间页VP1已经映射到物理内存页PP2处,所以CPU执行,OK,结果是3。完成!
       进程ADD执行1+3时,从操作系统保存的数据结构2中可以查到映射关系2,
       由于其进程虚拟空间页VP7已经映射到物理内存页PP3处,所以CPU执行,OK,结果是4。完成!
       进程ADD执行1+4时,从操作系统保存的数据结构2中查不到VP2与物理内存对于的映射关系。
       页错误产生了
      
    • 页错误处理

      • CPU控制权由进程ADD交给操作系统,操作系统去查询保存的数据结构1(保存的是可执行文件到虚拟地址空间的映射)

      • 通过该DP0页面所在的VMA虚拟内存区域,然后在VMA计算出页面在ELF(可执行文件)中的偏移,最后从物理内存中分配一个物理页面,并将该物理页面和进行虚拟地址建立映射关系。

      • 最后将CPU控制权交给ADD进程,从刚才页错误的地方继续执行,OK,结果是5,完成!

    • malloc

      • malloc就是memory allocate动态分配内存,malloc的出现时为了弥补静态内存分配的缺点,静态分配内存有如下缺点:

        • 传统的一维数组,如int a[5],使用传统的一维数组需要事先指定数组的长度,而且数组的长度必须是一个常量(宏定义的 常量)

        • 传统数组(静态分配),不能手动释放,只能等待系统释放,静态分配的变量在该函数内运行的时候有效,当静态分配的变量所在函数运行完之后,该内存会自动释放.

        • 静态分配的内存,是在栈中分配的,其实在C语言中的函数调用也是通过栈来实现的,栈这种数据结构的一个特点就是(先进后出),所以,在调用函数的时候,都是先压入栈中,然后,再从最上面的函数开始执行,最后,执行到main函数结束。动态分配通过malloc分配,是在堆中分配的,堆不是一种数据结构,它是一种排序方式,堆排序。

        • 传统数组的长度一旦定义之后,就不能更改,比如说,如果我有一个业务在这之前给分配的大小为100,但是,我现在由于业务数量的增长,原来的大小就无法满足。

        • 静态分配不能跨函数调用,就是无法在另一个函数中,来管理一个函数中的内存。静态分配,只在当前函数有效,当静态分配所在的函数运行完之后,该变量就不能被其他的函数所调用。

    • 使用

      • 使用malloc函数的时候,需要包含一个头文件#include <malloc.h>

      • malloc函数只接受一个形参如,int *p = (int )malloc(sizeof(int)).先来解释下这句话的含义,int
        p代表一个以int类型地址为内容的指针变量,p这个变量占4个字节(某些计算机),这个p变量是静态分配的一个变量。

      • 在某些计算机的前提下,指针变量所占的大小都是一样的,无论是char* 还是long*,因为,这些指针变量里面存放的是一个8位16进制的地址,所以占四个字节,当然这些都是在某些计算机的前提下,并不是所有的都是这样的。说道地址的话,就和计算机的地址总线有关,如果计算机的地址总线是32根,每根地址总线只有两种状态(1或0),32根地址线的话,如果全为1的话,刚好就是一个8位十六进制,一位十六进制等于四个二进制(2^4=16)。

      • 32根地址总线可以 表示210210210*22种状态,可以表示的最大内存为4G,也就是说32根地址总线(也就是四个字节
        的指针变量)最大可以表示4G内存

      • malloc函数会返回开辟空间的首地址,加(int *)的目的是让计算机知道,如何去划分这个开辟的空间,因为char、int
        、long这些类型的字节大小是不一样的,我们知道了首地址,还要知道是以几个字节为单元。

      • malloc开辟空间所返回的首地址是动态分配的。

    在这里插入图片描述
    在这里插入图片描述

    • static

      变量不加static修饰
      在这里插入图片描述
      在这里插入图片描述

      变量加static修饰
      在这里插入图片描述
      在这里插入图片描述

    • 总结:

      • 不加static修饰,函数或者代码块中的变量在函数或者代码块执行完毕后就直接回收销毁了,每次执行都会重新分配内存,每次都会销毁。

      • 加 static 修饰,函数或者代码块中的变量在函数或者代码块执行第一次初始化分配内存后,就算函数或者代码块执行完毕,该变量也不会被回收销毁,直到程序结束 static 变量才会被回收。

      • 当 static作用于函数定义时,或者用于代码块之外的变量声明时,static关键字用于修改标识符的链接属性。外部链接属性变为内部链接属性,标识符的存储类型和作用域不受影响。也就是说变量或者函数只能在当前源文件中访问,不能在其他源文件中访问。

      • 当static 作用于代码块内部的变量声明时,static关键字用于修改变量的存储类型。从自动变量变为静态变量,变量的属性和作用域不受影响

    展开全文
  • 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,非常难以捕捉。

    展开全文
  • CAN总线错误帧详解

    千次阅读 2021-10-09 07:49:09
    在发送和接收报文时,总线上的节点如果检测出了错误,那么该节点就会发送错误帧,通知总线上的节点,自己出错了。 错误帧由错误标志和错误界定符两个部分组成。 主动错误标志:6个连续的显性位; 被动错误标志:6个...

    1.错误帧的帧结构

    在发送和接收报文时,总线上的节点如果检测出了错误,那么该节点就会发送错误帧,通知总线上的节点,自己出错了。

    错误帧由错误标志和错误界定符两个部分组成。
    在这里插入图片描述
    主动错误标志:6个连续的显性位;
    被动错误标志:6个连续的隐性位;
    错误界定符:8个连续的隐性位。

    可以看到在错误标志之后还有0~6位的错误标志重叠,这一段最低有0个位,最多有6个位,关于这一段是怎么形成的,将在下文中解释。

    2.错误帧的检测

    2.1 位填充原则

    在了解CAN总线中的错误检测之前,首先需要了解什么是位填充。
    CAN协议中规定,当相同极性的电平持续五位时,则添加一个极性相反的位。

    图片

    对于发送节点:
    在发送数据帧和遥控帧时,对于SOF~CRC(除去CRC界定符)之间的位流,相同极性的电平如果持续5位,那么在下一个位插入一个与之前5位反型的电平;
    对于接收节点:
    在接收数据帧和遥控帧时,对于 SOF~CRC(除去CRC界定符) 之间的位流,相同极性的电平如果持续5位,那么需要删除下一位再接收。==
    ⚠️ 注意 :填充位的添加和删除是由发送节点和接收节点完成的,CAN-BUS只负责传输,不会操纵信号。

    3. 错误帧的种类

    在CAN总线通信中,一共有五种错误:位错误、ACK错误、填充错误、CRC错误、格式错误。

    3.1 位错误(Bit Check Error)

    节点将自己发送到总线上的电平与同时从总线上回读到的电平进行比较,如果发现二者不一致,那么这个节点就会检测出一个位错误。
    实际上所谓“发出的电平与从总线上回读的电平不一致”,指的就是节点向总线发出隐性位,却从总线上回读到显性位或者节点向总线发出显性位,却从总线上回读到隐性位这两种情况。
    Tips: 有三种例外情况不属于位错误:
    在仲裁区,节点向总线发送隐性位却回读到显性位,不认为是位错误,这种情况表示该节点仲裁失败;
    在ACK槽,节点向总线发送隐性位却回读到显性位,不认为是位错误,这种情况表示,该节点当前发送的这一帧报文至少被一个其它节点正确接收;
    该节点发送被动错误标志,节点Node_A向总线发送连续六个隐性位(被动错误标志)却回读到显性位,不认为是位错误。因为被动错误标志是六个连续的隐性位,所以在总线上按照线与机制,有可能这六个连续隐性位被其它节点发送的显性电平“吃掉”;

    3.2 ACK错误(Acknowledgment Error)

    按照CAN协议的规定,在一帧报文(数据帧或者遥控帧)发出之后,如果接收节点Node_B成功接收了该帧报文,那么接收节点Node_B就要在该帧报文ACK槽对应的时间段内向总线上发送一个显性位来应答发送节点Node_A。这样发送节点Node_A就会在ACK槽时间段内从总线上回读到一个显性位。因此:
    当发送节点Node_A在ACK槽时间段内没有回读到显性位,那么发送节点Node_A就会检测到一个ACK应答错误。这表示没有一个节点成功接收该帧报文。
    图片

    3.3 填充错误(Fill Error)

    在需要执行位填充原则的帧段(数据帧遥控帧的SOF~CRC序列),检测到连续六个同性位,则检测到一个填充错误。

    3.4 CRC错误

    发送节点Node_A在发送数据帧或者遥控帧时,会计算出该帧报文的CRC序列。接收节点Node_B在接收报文时也会执行相同的CRC算法,如果接收节点Node_B计算出的CRC序列值与发送节点Node_A发来的CRC序列值不一致,那么接收节点就检测到一个CRC错误。
    图片

    3.5格式错误

    在一帧报文发送时,如果在必须发送预定值的区域内检测到了非法值,那么就检测到一个格式错误。
    CAN报文中,有预定值的区域包括:

    • 数据帧和遥控帧的CRC界定符、ACK界定符、EOF;
    • 错误帧界定符
    • 过载帧界定符

    4.错误通知

    上一节中,讲到CAN通信中有五种错误,并且介绍了在什么情况下能够检测到这几种错误,在检测到错误之后,检测到错误的节点就要发送错误帧到总线上来通知总线上的其他节点。
    错误帧有的带有主动错误标志,有的带有被动错误标志,而且错误标志重叠部分的字节数也不一样,那么问题就来了:

    • 什么情况下发送带有主动错误标志的错误帧;

    • 什么情况下发送带有被动错误标志的错误帧;

    • 在哪个时间点发送错误帧;

    • 错误标志重叠部分是怎样形成的;

    4.1 节点错误状态

    按照CAN协议的规定,CAN总线上的节点始终处于以下三种状态之一。

    • 主动错误状态

    • 被动错误状态

    • 关闭状态
      当满足一定的条件时,节点可以从一种状态转换为另外一种状态。

    1)主动错误状态
    节点处于主动错误状态可以正常通信;
    处于主动错误状态的节点(可能是接收节点也可能是发送节点)在检测出错误时,发出主动错误标志。
    2)被动错误状态
    节点处于被动错误状态可以正常通信;
    处于被动错误状态的节点(可能是接收节点也可能是发送节点)在检测出错误时,发出被动错误标志。

    ⚠️注意:这里说处于主动错误状态或被动错误状态的节点仍然可以正常通信,这里的正常通信指的是:节点仍然能够从总线上接收报文,也能够竞争总线获胜后向总线上发送报文。但是不代表接收的报文一定正确也不代表一定能正确的发送报文。
    3)总线关闭状态
    节点处于总线关闭状态,那么该节点不能收发报文;
    处于总线关闭状态的节点,只能一直等待,在满足一定条件的时候,再次进入到主动错误状态。

    4.2 错误状态的转换

    现在我们知道:
    处于主动错误状态的节点在检测到错误时会发送带有主动错误标志的错误帧;
    处于被动错误状态的节点在检测到错误时会发送带有被动错误标志的错误帧。
    那么一个CAN节点在什么情况下处于主动错误状态,什么情况下处于被动错误状态呢?
    根据CAN协议的规定,在CAN节点内,有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。
    ⚠️注意:这两个计数器计得不是收发报文的数量,也不是收发错误帧的数量。TEC和RCE计数值的变化,是根据下表的规定来进行的
    图片

    CAN节点错误状态的转换,就是基于这两个计数器来进行的。
    图片

    可以看出,节点错误状态的转换就是一个“量变”到“质变”的过程:
    1) 主动错误状态
    最开始TCE和REC都小于127时,就处于主动错误状态。
    在这一状态下,节点检测到一个错误就会发送带有主动错误标志的错误帧,因为主动错误标志是连续六个显性位,所以这个时候主动错误标志将会“覆盖”掉总线上其它节点的发送,而之前在CAN总线上传输的报文就被这“六个连续显性位”破坏掉了。
    如果发出主动错误帧的节点是发送节点,这个情况下就相当于:刚刚发送的那一帧报文我发错了,现在我破坏掉它(发送主动错误帧),你们不管收到什么都不算数;
    如果发出主动错误帧的节点是接收节点,这个情况就相当于:刚刚我收报文的时候发现了错误,不管你们有没有发现这个错误,我现在主动站出来告诉大家这个错误,并把这一帧报文破坏掉(发送主动错误帧),刚才你们收到的东西不管对错都不算数了。
    Tips: 处于主动错误状态,说明这个节点目前是比较可靠的,出现错误的原因可能不是它本身的问题,即刚刚检测到的错误可能不仅仅只有它自己遇到,正是因为这一点,整个总线才相信它报告的错误,允许它破坏掉发送中的报文,也就是将这一次的发送作废。
    2)被动错误状态
    如果某个节点发送错误帧的次数较多,必将使得TCE>127 或者 REC>127,那么该节点就处于被动错误状态。

    在这一状态下,节点Node_A检测到一个错误就会发送带有被动错误标志的错误帧,因为被动错误标志是连续六个隐性位,所以这个时候总线上正在传输的报文位流不会受到该被动错误帧的影响,其它的节点该发送的发送,该接收的接收,没人搭理这个发送被动错误帧的节点Node_A。

    如果发出被动错误帧的节点Node_A为报文的发送节点,那么在发送被动错误帧之后,刚刚正在发送的报文被破坏,并且Node_A不能在错误帧之后随着连续发送刚刚发送失败的那个报文\。随之而来的是帧间隔,并且连带着8位隐性位的 “延迟传送” 段;这样总线电平就呈现出连续11位隐性位,总线上的其它节点就能判定总线处于空闲状态,就能参与总线竞争 。此时如果Node_A能够竞争成功,那么它就能接着发送,如果竞争不能成功,那么就接着等待下一次竞争。这种机制的目的正是为了让其它正常节点(处于主动错误)优先使用总线。
    Tips: 处于被动错误状态,说明这个节点目前是不太可靠的,出现错误的原因可能是它本身的问题,即刚刚检测到的错误可能仅仅只有它自己遇到,正是因为这一点,整个总线才不信任它报告的错误,从而只允许它发送六个连续的隐性位,这样它才不会拖累别人。
    3)总线关闭状态
    如果一个处于被动错误状态的节点,仍然多次发送被动错误帧,那么势必导致TEC > 255,这样就处于总线关闭状态。
    在总线关闭状态下的节点Node_A不能向总线上发送报文,也不能从总线上接收报文,整个节点脱离总线。等到检测到128次11个连续的隐性位时,TEC和REC置0,重新回到主动错误状态。

    按照我的理解这个所谓“检测到128次11个连续隐性位”其实就是让这个节点隔离一段时间冷静下,因为它一旦处于总线关闭状态,就不会和总线有任何的联系,这个时候只要它计算时间等于达到传送128次11个连续隐性位所用的时间,就可以重新连到总线上。

    ⚠️注意: 处于总线关闭状态说明,这个节点目前挂掉了,总线先把它踢开,这样它才不会拖累别人,等到它冷静一段时间之后再回到总线上。

    4.3 错误帧的发送

    在检测到错误之后,什么时候发送错误帧呢?
    按照CAN协议的规定:

    位错误、填充错误、格式错误、ACK错误。 在错误产生的那一位的下一位开始发送错误帧。

    CRC错误 紧随ACK界定符后的位发送错误帧

    例子1:
    图片

    (1)发送节点Node_A发送一个显性位,但是却从总线上听到一个隐形位,于是Node_A节点就会检测到一个位错误;
    (2)Node_A检测到位错误之后,立即在下一位开始发送主动错误帧:6个连续显性位的主动错误标志+8个连续隐性位的错误界定符;
    (3)对应Node_A发出的主动错误标志,总线上电平为6个连续显性位;
    (4)接收节点Node_B和Node_C从总线上听到连续6个显性位,那么就会检测到一个填充错误,于是这两个节点都会发送主动错误帧;
    (5)对应Node_B和Node_C发出的主动错误标志,总线电平又有6个连续显性电平,对应Node_B和Node_C发出的错误界定符,总线电平有8个连续的隐性电平。
    (6)在间歇场之后,Node_A节点重新发送刚刚出错的报文。

    例子2:
    图片

    从上图中可以看出错误帧之中,错误标志重叠部分是怎样形成的,这个例子中,位错误的错误标志与填充错误的错误标志重叠两位,剩下的部分还有四位:
    在这里插入图片描述

    展开全文
  • 我以前认为x86-64支持未对齐的内存访问...不过最近我用普通的mov指令观察到了总线错误.这是一个复制者:void test(void *a){asm("mov %0, %%rbp\n\t""mov 0(%%rbp), %%rdx\n\t": : "r"(a) : "rbp", "rdx");}int mai...
  • 当我试图提交或拉出此错误时发生总线错误(核心转储)!当我用gdb调试它时,(gdb git,运行commit -a,where)结果如下:muculus@muculus-laptop:~/www/pure/core$gdb gitGNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) ...
  • c 总线错误

    千次阅读 2010-11-28 17:08:00
    可能使用vc的程序员对这个错误比较陌生,因为vc并不提示总线错误,而是直接得出程序.exe遇到问题需要关闭的提示框。简单的从这个层面说,并没有提供太多有用的信息。但是如果使用gcc的程序员可能就不会陌生了,因为...
  • PCIe总线错误严重性=已更正

    千次阅读 2021-05-16 02:59:17
    当我按下Ctrl + Alt + F1时,我开始看到下图中显示的错误,它不允许我与控制台交互:我每次启动时都会看到这些错误一段时间。我需要按Ctrl + Alt + F1来访问非图形终端以安装一些Nvidia驱动程序。这是怎么回事?导致...
  • C中的总线错误和段错误

    千次阅读 2018-05-20 01:36:08
    这两个错误非常的折磨人,错误信息对引起这两种错误的源代码错误并未作简单的解释,上面的信息并未提供如何从代码中寻找错误的线索。所以往往很难定位到具体出错在哪里。 大多数的问题都出于这样一个事实:错误就是...
  • C语言53-LIN总线从机收发测试(STC32G-DEMO-CODE-220311kw)C语言53-LIN总线从机收发测试(STC32G-DEMO-CODE-220311kw)C语言53-LIN总线从机收发测试(STC32G-DEMO-CODE-220311kw)C语言53-LIN总线从机收发测试(STC...
  • 本文章是关于CAN总线的远程、错误帧的作用!
  • 我们分析一下,到底在那种情况下会出现I2C总线挂起的现象,然后给出相应的解决办法。
  • C语言54-LIN总线主机收发测试(STC32G-DEMO-CODE-220311kw)C语言54-LIN总线主机收发测试(STC32G-DEMO-CODE-220311kw)C语言54-LIN总线主机收发测试(STC32G-DEMO-CODE-220311kw)C语言54-LIN总线主机收发测试(STC...
  • 1.段错误: segmetation fault(core dumped)段错误相信大家都已很熟悉了,今天来对他小结一下。 ‘ 个人总结的常见的段错误的出现原因有: (1)使用了非法的内存地址或指针。包括使用了没有初始化的已经释放的...
  • 使用mmap遇到总线错误bus error

    千次阅读 2019-03-07 14:21:35
    先简单描绘一下错误发生的场景: #define MMAP_BUFF_SIZE 4096 struct Message { int len; char data[1024]; }; int fd = open("./test",O_RDWR | O_CREAT); lseek(fd,MMAP_BUFF_SIZE,SEEK_SET); ...
  • CAN总线【3】--CAN协议错误
  • 在以下几种情况下,I2C总线仲裁会失败。 (1)在地址或数据发送周期,当主设备输出“1”,而SDA被采样为“0”。 (2)在数据接收周期的应答位,当主设备输出“1”,而SDA被采样为“0”。 (3)当总线忙时,企图有一个...
  • “X型火控系统”根据实际需要采用了CAN总线方式来实现其内部单体间的通信。CAN总线是一种用于各种设备检测及控制的现场总线,它是一种多主总线,在高速网络和低成本的节点系统中应用都很广泛。
  • I2C总线连线图I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少...
  • 段错误(SIGSEGV)与总线错误(SIGBUS) 前言 最近整理组里的历史代码,发现有一段程序运行有问题,喜提Segmentation fault (core dumped),这个问题一般是访问非法内存造成的,但是使用gdb进行调试时,发现出现了...
  • 总线错误与段错误

    千次阅读 2017-02-19 15:48:18
    前段买了本《C专家编程》确实不错,特别是对内存思考这一章节,(O(∩_∩)O~目前我也只看到这一章节),其中讲到了总线错误和段错误: bus error(core dumped) 总线错误(信息已经转储) segmetation fault(core ...
  • C语言中 bus error(总线错误)解释

    千次阅读 2020-01-04 18:39:58
    bus error(总线错误): 当在ubuntu系统下编译程序遇到bus error ,这意味着你程序中的整数存储在错误的边界上,这时你应该仔细检查你的数据是否越界。 segmentation violation or memory fault : 这是提示你所编写...
  • 单介绍了CAN总线的特点与构成。详细介绍了主节点的工作过程和从节点硬件接口电路的搭建方法,并通过对微处理器的软件编程,实现了CAN总线网络的实时通信。整个节点通信系统工作稳定,可以满足现场环境要求。
  • 串行扩展总线技术是新一代单片机技术发展的一个显著特点,其中Philips公司推出的I2C总线最为著名。ST9+系列是意法半导体公司的单片机产品,能够很好地支持I2C总线协议。本文以ST9+单片机为例阐述I2C总线协议,并给出在...
  • I2C总线入门

    2018-04-11 10:14:23
    C 是Inter-Integrated Circuit的缩写,它是一种双向两线接口,一条 Serial Data Line (SDA) ,另一条Serial Clock (SCL),串行8位数据传输,上升沿采样
  • 串行扩展总线技术是新一代单片机技术发展的一个显著特点,其中Philips公司推出的I2C总线最为著名。ST9+系列是意法半导体公司的单片机产品,能够很好地支持I2C总线协议。本文以ST9+单片机为例阐述I2C总线协议,并给出...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,108
精华内容 32,043
关键字:

c 总线错误