2011-03-10 81 views
1

有时,在各种Unix体系结构中,在程序运行时重新编译程序会导致程序崩溃并显示“总线错误”。任何人都可以解释发生这种情况的条件吗首先,如何更新磁盘上的二进制文件对内存中的代码执行任何操作?我能想象的唯一情况是有些系统将代码映射到内存中,当编译器重写磁盘映像时,这会导致mmap无效。这种方法的好处是什么?通过更改可执行文件来使运行代码崩溃看起来非常不理想。重新编译二进制文件时发生总线错误

回答

2

在本地文件系统上,所有主流的类Unix系统都支持通过删除文件来解决此问题。旧的vnode保持打开状态,即使在目录条目消失之后重新用于新映像,旧文件仍然存在,未更改,现在未命名,直到最后一次引用它(在此情况下,内核)消失为止。

但是,如果你刚开始重写它,那么是的,它是mmap(3)'ed。当块被重写的两种情况之一可以取决于哪个MMAP发生(3)选项的动态链接程序使用:

  1. 内核将无效相应的页面,或
  2. 磁盘映像将改变,但现有的存储器页不会

无论哪种方式,正在运行的程序可能有麻烦。在第一种情况下,它基本上保证爆炸,而在第二种情况下,除非所有的页面都被引用,分页并且从不丢弃,否则它将被破坏。

有两个mmap标志旨在解决这个问题。一个是MAP_DENYWRITE(防止写入),另一个是MAP_COPY,它保留了原始版本的纯粹版本,并防止作者更改映射图像。

但是出于安全原因,DENYWRITE已被禁用,并且在任何主要的类Unix系统中都没有执行COPY。

+0

因此编译器一般不会删除和重写,而是覆盖输出文件? – Lutorm 2011-03-10 18:56:43

0

这是一个有点复杂的情况,可能会发生在你的情况。这个错误的原因通常是内存对齐问题。总线错误在基于FreeBSD的系统中更常见。考虑一个场景,你有一个类似的结构,

struct MyStruct { char ch [29]; // 29字节 int32 i; // 4字节 }

所以这个结构的总大小应该是33个字节。现在考虑一个你有32字节缓存行的系统。此结构不能加载到单个缓存行中。现在考虑以下语句

Struct MyStruct abc; char * cptr = &abc; //结构起始处的char点指向 int32 * iptr =(cptr + 1)// iptr指向结构的第2个字节。

现在总体结构大小为33字节,您的int指针指向第2个字节,因此您可以从int指针读取32字节数据(因为分配的内存总大小为33字节)。但是当你尝试读取它时,如果结构被分配在缓存线的边界,那么操作系统不可能在一次调用中读取32个字节。由于当前缓存行仅包含31个字节的数据,其余1个字节位于下一个缓存行。这将导致一个无效的地址,并会给“布斯错误”。大多数操作系统通过在内部生成两个内存读取调用来处理这种情况,但一些Unix系统不处理这种情况。为避免这种情况,建议您注意内存对齐。大多数情况下,当您尝试将结构转换为另一种数据类型并尝试读取该结构的内存时,会发生这种情况。

该情况有点复杂,所以我不确定我是否可以用更简单的方式解释它。我希望你了解这种情况。

+0

感谢您的回答,但我不明白这与更改磁盘上的映像有何关系。代码运行良好,除非我在可执行文件运行时重新编译它。 – Lutorm 2011-03-10 18:55:29