什么是总线错误?它与分割错误不同吗?

“巴士错误”是什么?message的含义,它与段错误有什么不同?

428771 次浏览

这取决于你的操作系统、CPU、编译器以及其他因素。

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

我相信内核会引发SIGBUS 当应用程序显示数据时 数据总线上的不对中。我认为 那是因为大多数[?现代编译器 对于大多数处理器,垫/对齐 为程序员提供的数据 以前的对准问题(至少) 减轻,因此一个人看不见 这些天SIGBUS太频繁了(AFAIK).

来自:在这里

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

总线错误试图访问不可能存在的内存。您使用了一个对系统没有意义的地址,或者该操作的地址类型是错误的。

它通常表示未对齐的访问。

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

总线错误的一个经典实例是在某些体系结构上,例如SPARC(至少在一些sparc上,可能已经更改了),即当您执行错误对齐访问时。例如:

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

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

总线错误现在在x86上很少见,发生在你的处理器甚至不能尝试请求的内存访问时,通常是:

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

分段错误发生在访问不属于你的进程的内存时。它们非常常见,通常是以下原因造成的:

  • 使用指向已释放对象的指针。
  • 使用未初始化的伪指针。
  • 使用空指针。
  • 缓冲区溢出。

PS:更准确地说,不是操作指针本身会导致问题。它正在访问它所指向的内存(解引用)。

POSIX系统上,当代码页由于某种原因不能被换入时,也可以得到rt_signal_lease信号。

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

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

在这里,如果双引号("")中的字符串的大小大于buf大小,则会给出总线错误。

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

#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上它给出了一个总线错误。为什么?我真的不知道。

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 描述了 SIGBUS为:

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

mmap规范表示:

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

shm_open 说,生成大小为0的对象:

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

因此,在*map = 0处,我们将触及已分配对象的结束部分。

ARMv8 aarch64中未对齐的堆栈内存访问

这在SPARC的什么是总线错误?中提到过,但在这里我将提供一个更可重复的示例。

你所需要的是一个独立的aarch64程序:

.global _start
_start:
asm_main_after_prologue:
/* misalign the stack out of 16-bit boundary */
add sp, sp, #-4
/* access the stack */
ldr w0, [sp]


/* exit syscall in case SIGBUS does not happen */
mov x0, 0
mov x8, 93
svc 0

该程序然后在ThunderX2服务器机中引发Ubuntu 18.04 aarch64, Linux内核4.15.0上的SIGBUS。

不幸的是,我不能在QEMU v4.0.0用户模式上重现它,我不知道为什么。

该错误似乎是可选的,由SCTLR_ELx.SASCTLR_EL1.SA0字段控制,我已经总结了相关文档再往前一点

我在Mac OS X上出现总线错误的原因是我试图在堆栈上分配大约1Mb的内存。这在一个线程中工作得很好,但当使用openMP时,这驱动总线错误,因为Mac OS X有非常有限的非主线程的堆栈大小

当根目录为100%时,我得到一个总线错误。

我同意上面所有的答案。以下是我对BUS错误的2点看法:

总线错误不需要由程序代码中的指令引起。这可能发生在您正在运行二进制文件,并且在执行过程中,二进制文件被修改(被构建覆盖或删除等)。

验证是否属实

检查原因的一个简单方法是在构建输出目录中启动两个相同二进制文件的实例,并在它们启动后运行构建。这两个正在运行的实例都会在构建完成并替换二进制文件(两个实例当前正在运行的二进制文件)后不久崩溃,并报出SIGBUS错误。

根本原因

这是因为操作系统交换内存页,在某些情况下,二进制文件可能不完全加载在内存中。当操作系统试图从相同的二进制文件中获取下一页时,这些崩溃就会发生,但二进制文件自上次读取以来已经发生了变化。

对我来说,我不小心触发了一个“巴士错误”;通过不声明我的程序集返回到.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:

希望这能对某人有所帮助

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

它们还取决于操作系统和体系结构,具体是什么导致了它们。

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

SIGBUS是当内存映射成功时,你遇到了底层内存系统的问题(内存不足,该位置没有内存,对齐,smmu阻止访问等),即总线错误。

一个SIGBUS也可以与mmap文件,如果文件从系统中消失,例如,你mmap文件在一个可移动媒体上,它被拔出。

查看平台的一个好地方是siginfo.h标头,以了解信号子类型。 这个页面提供了一个概述。 https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/siginfo.h#L245 < / p >
/*
* 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
最后需要注意的是,所有信号都可以由用户生成,例如kill。 如果它是用户生成的,那么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)

一个值得注意的原因是,如果您试图mmap用户空间不允许访问的/dev/mem区域,则会返回SIGBUS。