Puis-je faire une copie sur écriture memcpy sous Linux?

J'ai du code où je copie fréquemment un gros bloc de mémoire, souvent après n'y avoir apporté que de très petites modifications.

J'ai implémenté un système qui suit les changements, mais j'ai pensé que ce serait bien, si possible, de dire au système d'exploitation de faire une "copie sur écriture" de la mémoire, et de le laisser faire une copie des parties qui changent. Cependant, alors que Linux fait de la copie en écriture, par exemple lorsque fork()ing, Je ne peux pas trouver un moyen de le contrôler et de le faire moi-même.

22
demandé sur Chris Jefferson 2009-10-14 13:21:46

6 réponses

Votre meilleure chance est probablement de mmap() les données d'origine dans le fichier, puis mmap() le même fichier à nouveau en utilisant MAP_PRIVATE.

16
répondu MSalters 2009-10-14 11:22:45

En fonction de ce que vous copiez exactement, une structure de données persistante {[2] } peut être une solution à votre problème.

4
répondu Jørgen Fogh 2009-10-14 09:53:26

Il est plus facile d'implémenter la copie en écriture dans un langage orienté objet, comme c++. Par exemple, la plupart des classes de conteneurs Qt sont de copie sur écriture.

Mais si bien sûr vous pouvez le faire en C aussi, c'est juste un peu plus de travail. Lorsque vous souhaitez affecter vos données à un nouveau bloc de données, vous ne faites pas de copie, mais vous copiez simplement un pointeur dans un strcut wrapper autour de vos données. Vous devez garder une trace dans vos blocs de données de l'état des données. Si vous changez quelque chose dans votre nouveau bloc de données, vous faites une copie" réelle " et modifiez le statut. Vous ne pouvez bien sûr plus utiliser les opérateurs simples comme " = " pour l'affectation, à la place, vous devez avoir des fonctions (en C++, vous feriez simplement une surcharge d'opérateur).

Une implémentation plus robuste devrait utiliser des compteurs de référence au lieu d'un simple drapeau, je vous laisse le soin de le faire.

Un exemple rapide et sale: Si vous avez un

struct big {
//lots of data
    int data[BIG_NUMBER];
}

Vous devez implémenter vous-même assign functions et getters/setters.

// 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;
}

Vous devez écrire constructeurs et destructeurs aussi. Je recommande vraiment c++ pour cela.

2
répondu Gunther Piez 2009-10-14 10:55:11

Le mécanisme copy-on-write utilisé par exemple par fork () est une fonctionnalité de la MMU (Memory Management Unit), qui gère la pagination de la mémoire pour le noyau. L'accès à la MMU est une opération privilégiée, c'est-à-dire ne peut pas être fait par une application d'espace utilisateur. Je ne connais pas non plus D'API de copie en écriture exportée vers l'espace utilisateur.

(là encore, Je ne suis pas exactement un gourou de L'API Linux, donc d'autres pourraient signaler les appels D'API pertinents que j'ai manqués.)

Edit: {[6] } et lo, MSalters se lève à l'occasion. ;-)

2
répondu DevSolar 2009-10-14 12:32:29

Vous devriez pouvoir ouvrir votre propre mémoire via/proc/$PID / mem, puis mmap() la partie intéressante avec MAP_PRIVATE à un autre endroit.

1
répondu user175104 2009-10-15 23:04:09

Voici un exemple de travail:

#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;
}

Je ne sais pas si je dois activer la section commentée, pour plus de sécurité.

0
répondu fadedbee 2012-06-13 16:07:50