segfault when using mmap
I'm trying to use mmap fo开发者_开发问答r the first time to store a tree object with a lot of data in it. The tree class basically contains a pointer to the root of class Node, and each Node instance has an array of pointers to it's children. I think mmap is doing what it is supposed to, because I can access the tree's constant members, but when I try to access the pointer to the root I get a segfault.
Here is how a create a tree with a root node:
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);
...
}
Here is how I access the Tree:
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();
...
The output of root->data() provides a segfault. Can anyone give me a hint to where I'm wrong? Please say if I'm not making my problem clear.
Thanks in advance.
Mads
This is a mess. You need to understand how new
and delete
work before attempting what you are trying to do.
Where to start.
map = (Tree*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
This just says, treat that bit of memory as if it is aTree
, you've not constructed a tree in that bit of memory.- When you call
new
, it allocates aNode
object, somewhere in memory and you hold a pointer to that in your tree, when it is re-opened elsewhere, the pointer is no longer valid.
You need to store all the nodes in your mapped block too... There is no real easy answer to this, except try to understand custom memory allocators.
When you call mmap()
, you are basically just getting a block of raw memory. Thus you can't naively dereference the pointer map
without invoking some type of undefined behavior ... simply casting the memory returned from mmap()
to type Tree*
like you've done does not actually construct a Tree
object in the mapped memory.
If you wish to construct, or even copy construct a Tree
object in the memory returned from mmap
, you may want to look into using placement new
. For instance, you could do something like the following:
void* map;
map = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);
//...error checking
Tree* tree = new(map) Tree(); //placement new syntax
That will actually default-construct a Tree
object in the memory returned from mmap
, and at that point, you can properly use the tree
pointer variable in order to further add nodes to the tree. For instance, the following code could now be invoked without creating undefined behavior:
Node* root = new Node("data");
tree->set_root(root);
With placement new
, an actual Tree
object is being pointed to by the variable tree
, and you can now properly dereference that pointer, as well as any methods associated with that object.
One major item to keep in mind though is that when using placement new
, you will have to manually call the destructor for your Tree
object, and you will have to manually free the memory that you've allocated via mmap()
... you can't call delete
on the Tree*
.
This won't work. If root()
is a virtual method, you are hosed, as the vtable pointers point to methods in the current process. It might work if you don't call any virtual methods, but it's probably undefined behavior.
It's probably better to share data between processes, rather than objects. Let the objects have a handle to the data, and shield the user of the object from the details of decoding the data. Node* root = new Tree(memory_mapped_memory);
精彩评论