使用mmap时的segfault

segfault when using mmap

本文关键字:segfault 时的 mmap 使用      更新时间:2023-10-16

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

以下是如何创建具有根节点的树:

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->data((的输出提供了一个segfault。有人能告诉我哪里错了吗?如果我没有把问题说清楚,请说出来。

提前谢谢。

Mads

这一团糟。在尝试你要做的事情之前,你需要了解newdelete是如何工作的

从哪里开始。

  1. map = (Tree*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);这只是说,把这段内存当作Tree来处理,你还没有在这段内存中构建一棵树
  2. 当您调用new时,它会在内存中的某个位置分配一个Node对象,并且您在树中持有指向该对象的指针,当它在其他位置重新打开时,该指针将不再有效

您还需要将所有节点存储在映射块中。。。除了尝试理解自定义内存分配器之外,没有真正简单的答案。

当您调用mmap()时,您基本上只得到一块原始内存。因此,如果不调用某种类型的未定义行为,就不能天真地取消引用指针map。。。像您所做的那样简单地将从mmap()返回的内存强制转换为类型Tree*实际上并不会在映射的内存中构造Tree对象。

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

void* map;
map = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);
//...error checking
Tree* tree = new(map) Tree(); //placement new syntax

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

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

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

不过,需要记住的一个主要事项是,当使用placement new时,您必须手动调用Tree对象的析构函数,并且必须手动释放通过mmap()分配的内存。。。您不能在Tree*上呼叫delete

这行不通。如果root()是一个虚拟方法,则会被冲洗,因为vtable指针指向当前进程中的方法。如果您不调用任何虚拟方法,它可能会工作,但它可能是未定义的行为。

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