使用不需要fork()的方式实现fork()的写时复制行为

12

我有一个大缓冲区:

char *buf = malloc(1000000000); // 1GB
如果我分叉了一个新进程,它会有一个缓冲区与父进程的缓冲区共享内存,直到其中一个写入为止。即使如此,内核只需要分配一个新的4KiB块,其余部分仍将继续共享。
我想复制buf,但我只会改变复制品的一点点内容。我想要写时复制的行为而不用分叉。(就像你在fork时免费获得的一样。)
这可能吗?

当然可以,但这并不是“免费”的 - 你必须自己进行内存管理并跟踪更改。 - Marc B
1
是的,我想要免费的。我在想是否有任何基于mmap的解决方案,或者可能是我甚至没有想到的东西。 - fadedbee
@wabepper 这取决于你用哪个定义。根据微软Windows的说法,是的,但根据最新版本的Mac OSX,它是1GB。差别不是很大,在大多数情况下,存储这么多信息的时候差异是微小的。 - Richard J. Ross III
另外,我只是想说“哞”! - Richard J. Ross III
参见:https://dev59.com/lGQn5IYBdhLWcg3wRVVs简而言之:在Linux上很困难。 - Rusty Shackleford
显示剩余4条评论
1个回答

12

你需要在磁盘上创建一个文件或者POSIX共享内存段(shm_open)作为这个块的载体。第一次映射时,使用MAP_SHARED。当你准备好复制并切换到COW模式时,再次调用mmap,并使用MAP_FIXEDMAP_PRIVATE来覆盖原始映射,并使用MAP_PRIVATE来创建第二个副本。这样可以获得所需的效果。


1
看起来非常令人鼓舞,但我无法让它工作。我在第13行遇到了总线错误。fd == 3。你能指出我的愚蠢错误吗?https://gist.github.com/2924412 - fadedbee
1
你需要使用 ftruncate 来给共享内存段设置大小。初始大小为零。 - R.. GitHub STOP HELPING ICE
1
我怀疑崩溃实际上发生在第17行,你在那里写入缓冲区。你的调试printf是无用的,因为你没有刷新输出,它们甚至没有以\n结尾。 - R.. GitHub STOP HELPING ICE
3
它可以工作了!https://gist.github.com/2924412 为什么要注释掉buf的重新映射?我好像不需要它。非常感谢。 - fadedbee
据我所知,它不起作用。它可能在某些操作系统上有效,但在Linux上,当您修改原始的MAP_SHARED映射时,MAP_PRIVATE仍将查看支持实际文件的页面。只有通过MAP_PRIVATE VMA修改数据时,MAP_PRIVATE页面才会从文件“分叉”。 - ysdx
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接