我可以在Linux上进行写时复制memcpy吗?

时间:2009-10-14 09:21:47

标签: c linux memory-management

我有一些代码,我经常复制一大块内存,通常只需对其进行非常小的更改。

我已经实现了一个跟踪更改的系统,但我认为如果可能的话,告诉操作系统对内存执行“copy-on-write”并让它只处理副本那些改变的部分然而,虽然Linux执行copy-on-write,例如fork()时,我找不到控制它并自己动手的方法。

6 个答案:

答案 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;
}

为了安全起见,我有点不确定是否需要启用已注释的部分。