使用mmap时出现段错误

时间:2011-10-03 11:04:07

标签: c++ segmentation-fault mmap

我第一次尝试使用mmap存储一个包含大量数据的树对象。树类基本上包含一个指向Node类的根的指针,每个Node实例都有一个指向它的子节点的数组。我认为mmap正在做它应该做的事情,因为我可以访问树的常量成员,但是当我尝试访问指向root的指针时,我得到了一个段错误。

以下是创建具有根节点的树的方法:

int main(int argc, char *argv[])
{
    Tree *map;
    ...
    map = (Tree*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED) {
        close(fd);
        perror("Error mmapping the file");
        exit(EXIT_FAILURE);
    }


    Node* root = new Node("data");
    map->set_root(root);
        ...
}

以下是我访问树的方法:

int main(int argc, char *argv[])
{
    int i;
    int fd;
    Tree *map;

    fd = open(FILEPATH, O_RDONLY);
    if (fd == -1) {
        perror("Error opening file for reading");
        exit(EXIT_FAILURE);
    }

    map = (Tree*)mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);

    if (map == MAP_FAILED) {
       close(fd);
       perror("Error mmapping the file");
       exit(EXIT_FAILURE);
    }

    Node* root = map->root();
    cout << root->data();
    ...

root-&gt; data()的输出提供了段错误。任何人都可以给我一个暗示我错的地方吗?如果我没有明白我的问题,请说明。

提前致谢。

的Mads

3 个答案:

答案 0 :(得分:5)

这是一团糟。在尝试尝试之前,您需要了解newdelete的工作原理。

从哪里开始。

  1. map = (Tree*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);这就是说,将那段内存视为Tree,你没有在那段内存中构建一棵树。
  2. 当你调用new时,它会在内存中的某个位置分配一个Node对象,并在树中保存一个指针,当它在其他地方重新打开时,指针不再有效
  3. 你需要存储映射块中的所有节点......除了尝试理解自定义内存分配器之外,没有真正简单的答案。

答案 1 :(得分:0)

这不起作用。如果root()是一个虚方法,那么你就会被清除,因为vtable指针指向当前进程中的方法。如果你不调用任何虚方法,可能工作,但它可能是未定义的行为。

在进程之间共享数据可能更好,而不是对象。让对象具有数据句柄,并保护对象的用户免受解码数据的细节。 Node* root = new Tree(memory_mapped_memory);

答案 2 :(得分:0)

当你致电mmap()时,你基本上只是获得一块原始内存。因此,在不调用某种类型的未定义行为的情况下,您无法天真地取消引用指针map ...只需将从mmap()返回的内存转换为类型Tree*,就像您已经完成的那样在映射的内存中构造一个Tree对象。

如果您希望在Tree返回的内存中构造,甚至复制构造mmap对象,您可能需要查看使用placement new。例如,您可以执行以下操作:

void* map;
map = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);

//...error checking

Tree* tree = new(map) Tree(); //placement new syntax

实际上,这将在Tree返回的内存中默认构造一个mmap对象,此时,您可以正确使用tree指针变量以进一步添加节点到了树上。例如,现在可以调用以下代码而不创建未定义的行为:

Node* root = new Node("data");
tree->set_root(root);

使用placement new,变量Tree指向一个实际的tree对象,您现在可以正确地取消引用该指针,以及与该对象关联的任何方法。

要记住的一个主要项目是,当使用placement new时,您必须手动调用Tree对象的析构函数,并且您必须手动释放您的内存。已通过mmap()分配...您无法在delete上致电Tree*