【问题标题】:segfault when using mmap使用 mmap 时的段错误
【发布时间】:2011-11-29 20:28:11
【问题描述】:

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

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

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() 的输出提供了一个段错误。谁能给我一个提示我错在哪里?如果我没有把我的问题说清楚,请说出来。

提前致谢。

疯狂

【问题讨论】:

  • 您的代码令人困惑。您必须使用main() 的版本,但它们是不同的——为什么第一个有new,而第二个没有?无论如何,鉴于您只是在普通的空闲存储上而不是在映射的内存中分配节点,因此您的代码不太可能按照您的想法进行。

标签: c++ segmentation-fault mmap


【解决方案1】:

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

从哪里开始。

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

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

【讨论】:

  • 一旦您开始在 mmap 区域内分配节点,您还会发现使用指针是有问题的(因为文件不一定在每个进程中映射到相同的地址) .您可以将地图视为节点数组,并存储索引而不是指针。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    当您调用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 对象的析构函数,并且必须手动释放通过 @ 分配的内存987654339@ ...你不能在Tree*上拨打delete

    【讨论】:

      猜你喜欢
      • 2018-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 2014-05-20
      • 2021-12-22
      • 2018-10-04
      相关资源
      最近更新 更多