看起来,您真正需要的是写时复制(copy-on-write)语义。这意味着默认情况下不会进行任何复制,但如果任何给定线程需要更改数据的某些部分,则在使用时将透明地对该页面进行复制。
写时复制将为您节省大量内存,如果您的数据足够大,这些memcpy调用将会产生影响:
- 相同数据(在页面级别上)不会重复——减少了工作集的大小
- 在实际需要之前,不会浪费提取/存储操作
DMA并不是解决此问题的方案,它主要用于设备主机或设备设备通信,而不是以可用方式向普通用户空间进程公开的内容。
相反,您可以使用POSIX共享内存来获得此行为。
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
int main() {
int fd = shm_open("/cowalloc", O_RDWR|O_CREAT, 0600);
shm_unlink("/cowalloc");
ftruncate(fd, 1024);
char *master = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
strcpy(master, "hello world, this is a demonstration of COW behaviour in Linux");
char *thread = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_NORESERVE, fd, 0);
printf("Master: %s\nThread: %s\n", master, thread);
printf("\nChanging in thread:\n");
strcpy(thread, "This is a private change");
printf("Master: %s\nThread: %s\n", master, thread);
return 0;
}
这里的基本思想是使用MAP_SHARED一次性进行所有数据的全局设置(假设从磁盘/网络或计算中加载)。然后,您可以再次使用相同的文件描述符调用mmap,为您认为可能需要写入本地副本的每个线程创建其他私有映射。
在这里使用MAP_NORESERVE标志是可选的 - 如果您只更改每个线程中成千上万个页面中的一个页面,则使用它以避免不必要地获取大量交换可能是有意义的。
(请注意,如果您正在从磁盘中加载,则可以通过简单地在文件上使用mmap来进一步优化此过程)。
当然,使用COW智能指针类型在对象级别上执行COW行为可能更加清晰和便携。