什么是总线错误?

时间:2008-10-17 14:48:44

标签: c unix segmentation-fault bus-error

“总线错误”消息的含义是什么,它与段错误有什么不同?

17 个答案:

答案 0 :(得分:223)

现在在x86上很少出现总线错误,并且当您的处理器甚至无法尝试请求的内存访问时发生错误,通常是:

  • 使用处理器指令,其地址不满足其对齐要求。

访问不属于您的进程的内存时会发生分段错误,它们非常常见,通常是以下结果:

  • 使用指向已解除分配的内容的指针。
  • 使用未初始化的虚假指针。
  • 使用空指针。
  • 溢出缓冲区。

PS:更确切地说,这不会操纵会导致问题的指针本身,而是访问它所指向的内存(取消引用)。

答案 1 :(得分:78)

段错误正在访问您不允许访问的内存。它是只读的,你没有权限等......

总线错误正在尝试访问不可能存在的内存。您使用了对系统无意义的地址,或者该操作的错误地址类型。

答案 2 :(得分:11)

  

我相信内核提出了SIGBUS   当应用程序显示数据时   数据总线上的错位。我认为   那是因为大多数[?]现代编译器   对于大多数处理器垫/对齐   程序员的数据,   以前的对齐问题(至少)   减轻了,因此人们看不到   SIGBUS这些天太常见了(AFAIK)。

来自:Here

答案 3 :(得分:8)

mmap最小POSIX 7示例

"总线错误"当内核将SIGBUS发送给进程时发生。

由于ftruncate被遗忘而产生它的最小例子:

#include <fcntl.h> /* O_ constants */
#include <unistd.h> /* ftruncate */
#include <sys/mman.h> /* mmap */

int main() {
    int fd;
    int *map;
    int size = sizeof(int);
    char *name = "/a";

    shm_unlink(name);
    fd = shm_open(name, O_RDWR | O_CREAT, (mode_t)0600);
    /* THIS is the cause of the problem. */
    /*ftruncate(fd, size);*/
    map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    /* This is what generates the SIGBUS. */
    *map = 0;
}

使用以下命令运行:

gcc -std=c99 main.c -lrt
./a.out

在Ubuntu 14.04中测试。

POSIX describes SIGBUS as:

  

访问内存对象的未定义部分。

mmap spec说:

  

地址范围内的引用,从pa开始并继续len字节到对象结束后的整个页面,将导致传送SIGBUS信号。

shm_open says that它会生成大小为0的对象:

  

共享内存对象的大小为零。

所以在*map = 0我们正在触及分配对象的末尾。

答案 4 :(得分:6)

当代码页由于某种原因无法被分页时,您也可以获得SIGBUS。

答案 5 :(得分:3)

某个架构错误的一个典型实例是某些架构,例如SPARC(至少某些SPARC,可能已经更改),就是当您进行错误对齐的访问时。例如:

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;

此代码段尝试将32位整数值0xdeadf00d写入(很可能)未正确对齐的地址,并在这方面“挑剔”的架构上生成总线错误。顺便说一句,英特尔x86是这样的架构,它允许访问(尽管执行速度更慢)。

答案 6 :(得分:2)

通常表示未对齐访问。

尝试访问实际上不存在的内存也会导致总线错误,但如果您使用的是带有MMU的处理器和没有错误的操作系统,则不会看到此错误,因为您不会将任何不存在的内存映射到进程的地址空间。

答案 7 :(得分:2)

这取决于您的操作系统,CPU,编译器以及可能的其他因素。

一般来说,这意味着CPU总线无法完成命令或遇到冲突,但这可能意味着一系列事情,具体取决于运行的环境和代码。

- 亚当

答案 8 :(得分:2)

我在OS X上编程C时遇到的总线错误的一个具体示例:

#include <string.h>
#include <stdio.h>

int main(void)
{
    char buffer[120];
    fgets(buffer, sizeof buffer, stdin);
    strcat("foo", buffer);
    return 0;
}

如果您不记得文档strcat通过更改第一个参数(翻转参数并且工作正常)将第二个参数附加到第一个参数。在linux上,这会产生分段错误(如预期的那样),但在OS X上会出现总线错误。为什么?我真的不知道。

答案 9 :(得分:2)

当根目录为100%时,我收到了总线错误。

答案 10 :(得分:1)

我在Mac OS X上出现总线错误的原因是我试图在堆栈上分配大约1Mb。这在一个线程中运行良好,但是当使用openMP时,这会导致总线错误,因为Mac OS X的限制非常有限stack size for non-main threads

答案 11 :(得分:0)

首先,SIGBUS 和 SIGSEGV 不是特定类型的错误,而是错误组或错误族。这就是为什么您通常会看到信号编号 (si_no) 和信号代码 (si_code)。

它们还取决于操作系统和架构究竟是什么导致它们。

通常我们可以这么说。 SIGSEGV 与内存映射(权限,无映射)相关,即 mmu 错误。

SIGBUS 是当内存映射成功并且您遇到底层内存系统问题(内存不足、该位置没有内存、对齐、smmu 阻止访问等)时,即总线错误..< /p>

如果文件从系统中消失,SIGBUS 也可以带有 mmapped 文件,例如您将文件映射到可移动媒体上,然后它就会被拔掉。

在平台上查看的好地方是 siginfo.h 头文件,以了解信号子类型。 例如for linux 本页提供了概述。 https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/siginfo.h#L245

/*
 * SIGSEGV si_codes
 */
#define SEGV_MAPERR 1   /* address not mapped to object */
#define SEGV_ACCERR 2   /* invalid permissions for mapped object */
#define SEGV_BNDERR 3   /* failed address bound checks */
#ifdef __ia64__
# define __SEGV_PSTKOVF 4   /* paragraph stack overflow */
#else
# define SEGV_PKUERR    4   /* failed protection key checks */
#endif
#define SEGV_ACCADI 5   /* ADI not enabled for mapped object */
#define SEGV_ADIDERR    6   /* Disrupting MCD error */
#define SEGV_ADIPERR    7   /* Precise MCD exception */
#define SEGV_MTEAERR    8   /* Asynchronous ARM MTE error */
#define SEGV_MTESERR    9   /* Synchronous ARM MTE exception */
#define NSIGSEGV    9

/*
 * SIGBUS si_codes
 */
#define BUS_ADRALN  1   /* invalid address alignment */
#define BUS_ADRERR  2   /* non-existent physical address */
#define BUS_OBJERR  3   /* object specific hardware error */
/* hardware memory error consumed on a machine check: action required */
#define BUS_MCEERR_AR   4
/* hardware memory error detected in process but not consumed: action optional*/
#define BUS_MCEERR_AO   5
#define NSIGBUS     5

最后要注意的是,所有信号也可以由用户生成,例如杀。 如果它是用户生成的,则 si_code 是 SI_USER。所以特殊来源得到负 si_codes。

/*
 * si_code values
 * Digital reserves positive values for kernel-generated signals.
 */
#define SI_USER     0       /* sent by kill, sigsend, raise */
#define SI_KERNEL   0x80        /* sent by the kernel from somewhere */
#define SI_QUEUE    -1      /* sent by sigqueue */
#define SI_TIMER    -2      /* sent by timer expiration */
#define SI_MESGQ    -3      /* sent by real time mesq state change */
#define SI_ASYNCIO  -4      /* sent by AIO completion */
#define SI_SIGIO    -5      /* sent by queued SIGIO */
#define SI_TKILL    -6      /* sent by tkill system call */
#define SI_DETHREAD -7      /* sent by execve() killing subsidiary threads */
#define SI_ASYNCNL  -60     /* sent by glibc async name lookup completion */

#define SI_FROMUSER(siptr)  ((siptr)->si_code <= 0)
#define SI_FROMKERNEL(siptr)    ((siptr)->si_code > 0)

答案 12 :(得分:0)

对我来说,我不小心触发了“总线错误”,因为我没有声明我的程序集正在返回 .text 部分。这似乎很明显,但它让我难倒了一段时间。

例如

.globl _myGlobal # Allocate a 64-bit global with the value 2
.data
.align 3
_myGlobal:
.quad 2
.globl _main # Main function code
_main:
push %rbp

从数据返回代码时缺少文本指令:

_myGlobal:
.quad 2
.text # <- This
.globl _main
_main:

希望这对某人有所帮助

答案 13 :(得分:0)

我同意以上所有答案。这是我关于BUS错误的2美分:

程序代码中的指令不必引起BUS错误。当您运行二进制文件并且在执行过程中,二进制文件被修改(被构建覆盖或删除等)时,可能会发生这种情况。

验证是否是这种情况: 检查是否是原因的一种简单方法是启动运行相同二进制文件的实例并运行构建。构建完成并替换二进制文件(两个实例当前都在运行的二进制文件)后不久,两个正在运行的实例都将崩溃并出现SIGBUS错误。

根本原因: 这是因为OS交换内存页面,并且在某些情况下,整个二进制文件可能位于内存中,并且当OS尝试从同一二进制文件中获取下一页时,这些崩溃将发生,但是二进制文件自上次读取以来已更改。 / p>

答案 14 :(得分:0)

我刚刚发现,在ARMv7处理器上你可以编写一些代码,在未经优化的情况下为你提供分段错误,但在使用-O2编译时会出现总线错误(优化更多)。 我正在使用ubuntu x64的gcc arm gnueabihf交叉编译器。

答案 15 :(得分:0)

要添加上面回答的blxtd,当您的进程无法尝试访问特定“变量”的内存时,也会发生总线错误。

for (j = 0; i < n; j++) {
                for (i =0; i < m; i++) {
                        a[n+1][j] += a[i][j];
                }
        }

请注意 无意 变量'i' 的使用首先'for loop'? 这就是造成总线错误的原因。

答案 16 :(得分:-1)

导致总线错误的典型缓冲区溢出是

{
    char buf[255];
    sprintf(buf,"%s:%s\n", ifname, message);
}

如果双引号(“”)中字符串的大小超过buf大小,则会出现总线错误。