什么是总线错误?

ral*_*ldi 245 c unix segmentation-fault bus-error

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

blt*_*txd 232

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

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

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

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

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

  • 它们并不罕见; 我刚刚参加了练习9,从如何学习艰难的方式,已经遇到过一个...... (96认同)
  • 总线错误的另一个原因(无论如何在Linux上)是操作系统无法使用物理内存支持虚拟页面(例如,低内存条件或使用大页面内存时的大页面.)通常mmap(和malloc)只是保留虚拟地址空间,内核根据需要分配物理内存(所谓的软页面错误.)制作足够大的malloc,然后写入足够的内容,你将得到一个总线错误. (22认同)
  • 在我的情况下,方法“ static_cast”为存储回调的对象添加了“ void *”参数(一个属性指向该对象,另一个属性指向该方法)。然后调用该回调。但是,作为“ void *”传递的内容完全不同,因此方法调用导致总线错误。 (2认同)

Cli*_*rce 82

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

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


Oli*_*Oli 11

我相信当应用程序在数据总线上出现数据错位时,内核会引发SIGBUS.我认为,由于大多数处理器的大多数[?]现代编译器为程序员填充/对齐数据,因此过去(至少)减少了对齐问题,因此现在人们看不到SIGBUS(AFAIK).

来自:这里

  • 取决于您对代码所做的讨厌的技巧。如果你做一些愚蠢的事情,比如做指针数学,然后类型转换以访问问题模式,你可以触发 BUS 错误/对齐陷阱到 short、int 或 long 并尝试访问有问题的结果。)X86 系统几乎可以让您这样做,尽管会造成真正的性能损失。*某些* ARMv7 系统将允许您执行此操作 - 但大多数 ARM、MIPS、Power 等系统会因此而抱怨您。 (2认同)

Cir*_*四事件 10

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;
}
Run Code Online (Sandbox Code Playgroud)

运行:

gcc -std=c99 main.c -lrt
./a.out
Run Code Online (Sandbox Code Playgroud)

在Ubuntu 14.04中测试过.

POSIX 描述 SIGBUS为:

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

MMAP规范说:

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

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

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

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


Jos*_*hua 6

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

  • 当我在运行进程时更新.so文件时,通常会发生这种情况 (6认同)

unw*_*ind 5

总线错误的一个典型实例是在某些体系结构上,例如SPARC(至少有一些 SPARC,也许这已被更改),即当您进行未对齐的访问时。例如:

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;
Run Code Online (Sandbox Code Playgroud)

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

  • @Supercat:问题是这样的——你在 X86 上就可以成功。你在 ARM、MIPS、Power 等上尝试这个,你会遇到一些糟糕的事情。在低于 Arch V7 的 ARM 上,您的代码将出现对齐失败,而在 V7 上,如果您的运行时设置为它,则可以通过严重的性能影响来处理它。你只是不想这样做。坦率地说,这是不好的做法。:D (3认同)
  • 我不会用“脆弱”这个词来形容这一切。X86 机器和代码有一段时间让人们做一些相当愚蠢的事情,这就是其中之一。如果您遇到此类问题,请重新考虑您的代码 - 首先它在 X86 上的性能不是很好。 (2认同)

Eri*_*aas 5

我刚刚在 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • “foo”存储在内存的只读段中,因此无法对其进行写入。它不是堆栈溢出保护,只是内存写保护(如果您的程序可以自行重写,这就是一个安全漏洞)。 (3认同)

Adi*_*lli 5

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

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

验证是否是这种情况

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

根本原因

这是因为操作系统会交换内存页面,并且在某些情况下,二进制文件可能不会完全加载到内存中。当操作系统尝试从同一个二进制文件中获取下一页时,就会发生这些崩溃,但该二进制文件自上次读取以来已发生更改。

  • 同意,根据我的经验,这是总线错误的最常见原因。 (2认同)