我可以在Linux的write memcpy上复制吗?

我可以在Linux的write memcpy上复制吗?,c,linux,memory-management,C,Linux,Memory Management,我有一些代码,我经常复制一大块内存,通常是在对它做了非常小的更改之后 我已经实现了一个跟踪更改的系统,但我认为如果可能的话,告诉操作系统对内存进行“写时拷贝”,让它只处理那些更改部分的拷贝,这可能会很好。然而,虽然Linux确实在写时复制,例如在fork()调用时,我找不到控制它并自己执行的方法。fork()使用的写时复制机制是MMU(内存管理单元)的一个功能,它处理内核的内存分页。访问MMU是一项特权操作,即不能由用户空间应用程序完成。我也不知道有任何写入时拷贝API导出到用户空间 (再说一遍

我有一些代码,我经常复制一大块内存,通常是在对它做了非常小的更改之后


我已经实现了一个跟踪更改的系统,但我认为如果可能的话,告诉操作系统对内存进行“写时拷贝”,让它只处理那些更改部分的拷贝,这可能会很好。然而,虽然Linux确实在写时复制,例如在fork()调用时,我找不到控制它并自己执行的方法。

fork()使用的写时复制机制是MMU(内存管理单元)的一个功能,它处理内核的内存分页。访问MMU是一项特权操作,即不能由用户空间应用程序完成。我也不知道有任何写入时拷贝API导出到用户空间

(再说一遍,我并不是Linux API的专家,所以其他人可能会指出我错过的相关API调用。)


编辑:看,MSalters随情况而变化。;-)

> P>根据您复制的具体内容,A可能是解决问题的一种解决方案。

在面向对象语言(如C++)中更容易实现复制写。例如,Qt中的大多数容器类都是写时复制的

当然,如果你也能在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++。对于这个,你最好的机会可能是:<代码> MAMP()/<代码>原始数据到文件,然后<代码> MAMP()/Cuth>再次使用“代码> MaPuxBuy相同的文件。

< P>你应该可以通过/PROC/$$PID /MEM,然后MAMP()打开你自己的内存。有趣的是,它将地图私密到其他地方。

以下是一个工作示例:

#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
#定义尺寸4096
内部主(空){
int fd=shm_open(“/tmpmem”,O_RDWR | O_CREAT,0666);
int r=ftruncate(fd,大小);
printf(“fd:%i,r:%i\n”,fd,r);
char*buf=mmap(空、大小、保护读取、保护写入、,
MAP_共享,fd,0);
printf(“调试0\n”);
buf[尺寸-2]=41;
buf[尺寸-1]=42;
printf(“调试1\n”);
//不知道为什么需要这样做,或者工作
//r=mmap(buf、大小、保护读取、保护写入、,
//地图固定,fd,0);
//printf(“r:%i\n”,r);
char*buf2=mmap(空值、大小、保护读取、保护写入、,
MAP_PRIVATE,fd,0);
printf(“buf2:%i\n”,buf2);
buf2[尺寸-1]=43;
buf[尺寸-2]=40;
printf(“buf[-2]:%i,buf[-1]:%i,buf2[-2]:%i,buf2[-1]:%i\n”,
buf[尺寸-2],
buf[尺寸-1],
buf2[尺寸-2],
buf2[尺寸-1]);
取消链接(fd);
返回退出成功;
}

出于安全考虑,我有点不确定是否需要启用已注释掉的部分。

您正在复制什么类型的数据?写时复制可能不是唯一的解决方案。请注意,您需要创建两个
MAP\u PRIVATE
映射-COW语义要求所有用户都有COW副本,没有人使用“主”副本。不幸的是,文件本身似乎是必要的。为什么?假设母版是
AA
,而COW的原因是您需要一份可以更改为
AB
的副本。没有理由将原始的
AA
作为私有映射,因为没有人打算对其进行写入。这仅仅是一个模板。我的评论是基于“原始”副本也可能被写入的可能性,在这种情况下,如果这些更改是否反映在COW副本中,则无法确定。顺便说一句,很遗憾,
mmap
没有提供这方面的内在支持-我可能会尝试向
mmap
添加对复制现有映射的支持,看看它是如何运行的。我支持MSalters:没有“标准”的COW语义集。将一个映射作为“真实”文件,另一个映射作为私有副本似乎是完全合理的。显然,有些应用程序需要可写快照或诸如此类的东西,但不是全部。可以用来解决创建文件的需要,但您仍然需要分配原始数据memfd备份内存来cow它。这不会生成我想要的cow语义,如果操作系统这样做了,它只会复制(在Mac OS X上至少)已更改的4k页面,剩下的(其他10s或100s MB)数据结构仍然很复杂。当然,我可以,而且已经实现了我真正想要的,但如果我能让操作系统为我实现,那就太好了。linux内核的更新版本可能会自动实现,内核2.6.32+有代码可以用写时复制链接替换重复页面,然而,ksm子系统还不是很成熟,直到现在它的工作方式是相反的:页面在被复制和替换(如果相同)后被扫描。如果你想让它从用户空间控制它,你可能想看看linux/mm/ksm.c并做出你需要的更改。发布的解决方案实际上根本不是“CoW”,它是一个软件仿真,它通过一个间接层强制所有“写”操作。我相信Chris特别要求使用MMU硬件的内存级解决方案。FWIW:您不需要新版本的Linux内核。BSD mmap()支持MAP_PRIVATE已有几十年了——它从一开始就是POSIX的一部分。这不会改变