我想分配并初始化相当大的连续内存块(~1GB),然后将其标记为只读并分叉多个(比如几十个)将使用它的子进程,而不会创建自己的副本内存(机器没有足够的内存)。
我是否正确地认为,如果我像往常一样malloc
内存,然后使用mprotect(addr, size, PROT_READ)
然后fork
将其标记为只读,这将允许子进程安全使用内存而不会导致它被复制? (假设我确保在mprotect
调用后没有尝试写入分配的内存。
编辑:感谢您的所有答案。
后续问题 - 我计划使用shmget
,但我认为它使用mm
,因此仅限于较小的分配(请参阅Restrictions section of this page)。例如/proc/sys/kernel/shmmax
在服务器上是32MB我正在使用这个。但我想要1GB的连续内存。我错了这个限制吗?
答案 0 :(得分:7)
实现将要求 addr 是sysconf()返回的页面大小的倍数。
如果未通过调用mmap()建立映射,则未指定此函数的行为。
mprotect
仅适用于页面,而不适用于任意字节范围,因此通常malloc
不合适。 posix_memalign
可能会有所帮助,但是...... mprotect
任何您自己mmap
未提及的内容。请改用mmap(0, pages*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)
。答案 1 :(得分:3)
由于任何子进程都可以调用mprotect()
来删除保护并开始在那里写作,所以你不对。如果页面尚未复制,则会违反fork()
。
即使它的工作方式是将写时复制用于分叉进程,我也不认为 standards 中的任何地方都这样说(POSIX并没有说它是复制的) - 例如,写。)
您可以使用标准度量来共享内存,而不是使用非标准行为。例如,POSIX与shm_open
和后续mmap
共享内存(正如评论中指出的那样,post由ephemient解释)。文件描述符将通过分叉保存。
答案 2 :(得分:2)
没有必要将其标记为只读,只需让您的子进程单独使用即可。
如果父母和孩子都没有写信给它,它应该保持共享状态。如果你不想改变它,那很好。
如果你想写信,你会想要使用带有MAP_SHARED的mmap。
答案 3 :(得分:1)
您假设内核将执行 copy-on-write 优化,而不是复制mprotect
页面。不过我会指望它。 malloc
- 内存中包含各种各样的元数据 - 保护页面等等,只有Ulrich Drepper知道libc内部发生了什么:)
在磁盘文件中准备数据并将它们mmap
放入所有进程中,或者只是转到正常的POSIX shm_open
路径可能更容易,更安全。
答案 4 :(得分:0)
我的理解是肯定的,因为Linux对传递给子进程的内存页使用了写时复制机制。
答案 5 :(得分:0)
你可以这样做。
另一种方法是使用mmap()。
另一种方法是使用POSIX共享内存(shm_open());另一个主要替代方案是System V共享内存(shmget(),shmat())。正式共享内存系统的一个优点是您的父进程可以创建内存,然后无关的进程可以连接到它 - 如果这是有益的。