我有一些代码,我经常复制一大块内存,通常只需对其进行非常小的更改。
我已经实现了一个跟踪更改的系统,但我认为如果可能的话,告诉操作系统对内存执行“copy-on-write”并让它只处理副本那些改变的部分然而,虽然Linux执行copy-on-write,例如fork()时,我找不到控制它并自己动手的方法。
答案 0 :(得分:17)
最好的机会可能是mmap()
要提交的原始数据,然后使用mmap()
再次MAP_PRIVATE
同一文件。
答案 1 :(得分:4)
根据您要复制的具体内容,persistent data structure可能是解决您问题的方法。
答案 2 :(得分:2)
采用的写时复制机制,例如by fork()是MMU(内存管理单元)的一个特性,它处理内核的内存分页。访问MMU是特权操作,即不能由用户空间应用程序完成。我不知道任何导出到用户空间的写时复制API。
(然而,我并不是Linux API上的大师,所以其他人可能会指出我错过的相关API调用。)
编辑:而且,MSalters上升到了这个场合。 ; - )
答案 3 :(得分:2)
在面向对象语言(如c ++)中实现copy-on-write更容易。例如,Qt中的大多数容器类都是copy-on-write。
但是如果当然你也可以在C中做到这一点,那只是一些工作。如果要将数据分配给新数据块,则不要复制,而只需在数据的包装器条带中复制指针。您需要在数据块中跟踪数据状态。如果您现在更改新数据块中的某些内容,则可以制作“真实”副本并更改状态。 您当然不能再使用像“=”这样的简单运算符来进行赋值,而是需要有函数(在C ++中你只需要运算符重载)。
更强大的实现应该使用引用计数器而不是简单的标志,我留给你。
一个快速而肮脏的例子: 如果你有
struct big {
//lots of data
int data[BIG_NUMBER];
}
你必须自己实现赋值函数和getter / setter。
// assume you want to implent cow for a struct big of some kind
// now instead of
struct big a, b;
a = b;
a.data[12345] = 6789;
// you need to use
struct cow_big a,b;
assign(&a, b); //only pointers get copied
set_some_data(a, 12345, 6789); // now the stuff gets really copied
//the basic implementation could look like
struct cow_big {
struct big *data;
int needs_copy;
}
// shallow copy, only sets a pointer.
void assign(struct cow_big* dst, struct cow_big src) {
dst->data = src.data;
dst->needs_copy = true;
}
// change some data in struct big. if it hasn't made a deep copy yet, do it here.
void set_some_data(struct cow_big* dst, int index, int data } {
if (dst->needs_copy) {
struct big* src = dst->data;
dst->data = malloc(sizeof(big));
*(dst->data) = src->data; // now here is the deep copy
dst->needs_copy = false;
}
dst->data[index] = data;
}
您还需要编写构造函数和析构函数。我真的推荐c ++。
答案 4 :(得分:1)
您应该可以通过/ proc / $ PID / mem打开自己的内存,然后使用MAP_PRIVATE将其有趣的部分mmap()打开到其他地方。
答案 5 :(得分:0)
这是一个有效的例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 4096
int main(void) {
int fd = shm_open("/tmpmem", O_RDWR | O_CREAT, 0666);
int r = ftruncate(fd, SIZE);
printf("fd: %i, r: %i\n", fd, r);
char *buf = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
printf("debug 0\n");
buf[SIZE - 2] = 41;
buf[SIZE - 1] = 42;
printf("debug 1\n");
// don't know why this is needed, or working
//r = mmap(buf, SIZE, PROT_READ | PROT_WRITE,
// MAP_FIXED, fd, 0);
//printf("r: %i\n", r);
char *buf2 = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
printf("buf2: %i\n", buf2);
buf2[SIZE - 1] = 43;
buf[SIZE - 2] = 40;
printf("buf[-2]: %i, buf[-1]: %i, buf2[-2]: %i, buf2[-1]: %i\n",
buf[SIZE - 2],
buf[SIZE - 1],
buf2[SIZE - 2],
buf2[SIZE - 1]);
unlink(fd);
return EXIT_SUCCESS;
}
为了安全起见,我有点不确定是否需要启用已注释的部分。