¿Puedo hacer un memcpy de copia en escritura en Linux?

7 minutos de lectura

Tengo un código en el que con frecuencia copio un gran bloque de memoria, a menudo después de realizarle solo cambios muy pequeños.

Implementé un sistema que realiza un seguimiento de los cambios, pero pensé que sería bueno, si es posible, decirle al sistema operativo que haga una ‘copia en escritura’ de la memoria, y dejar que se ocupe solo de hacer una copia de esas partes. que cambio. Sin embargo, aunque Linux copia en escritura, por ejemplo, cuando se bifurca (), no puedo encontrar una manera de controlarlo y hacerlo yo mismo.

  • ¿Qué tipo de datos estás copiando? Copy-on-write podría no ser la única solución.

    –Jorgen Fogh

    14 de octubre de 2009 a las 9:54

Su mejor oportunidad es probablemente mmap() los datos originales al archivo, y luego mmap() el mismo archivo de nuevo usando MAP_PRIVATE.

  • Tenga en cuenta que necesita crear dos MAP_PRIVATE asignaciones: la semántica de COW requiere que todos los usuarios tengan copias de COW, sin que nadie use una copia “maestra”. Desafortunadamente, el archivo en sí parece ser necesario.

    – café

    14 de octubre de 2009 a las 12:11

  • ¿Por qué? Supongamos que el maestro es AAy el motivo de COW es que desea una copia que puede cambiar a AB. No hay razón para que el original AA necesita ser un mapeo privado, ya que nadie planea escribir en él. Es simplemente una plantilla.

    – MSalters

    14 de octubre de 2009 a las 12:16

  • Mi comentario se basó en la posibilidad de que también se pueda escribir en la copia “original”, en cuyo caso no se especificaría si esos cambios se reflejan en la copia COW o no. Aparte, es una lástima que mmap no proporciona soporte inherente para esto; podría jugar agregando soporte a mmap para duplicar asignaciones existentes y ver cómo va.

    – café

    14/10/2009 a las 22:20

  • Estoy con MSalters: no hay un conjunto “estándar” de semántica COW. Hacer que una asignación sea el archivo “real” y otra sea una copia privada parece perfectamente razonable. Obviamente, algunas aplicaciones necesitan instantáneas grabables o lo que sea, pero no todas.

    –Andy Ross

    15 de octubre de 2009 a las 23:07

  • memfd_create se puede usar para evitar la necesidad de crear un archivo, pero aún necesita asignar la memoria respaldada por memfd de datos original para ocultarlo.

    – the8472

    04/07/2018 a las 20:31

Dependiendo de qué es exactamente lo que está copiando, un estructura de datos persistente podría ser una solución para su problema.

avatar de usuario
Gunther piez

Es más fácil implementar copy-on-write en un lenguaje orientado a objetos, como c++. Por ejemplo, la mayoría de las clases de contenedor en Qt son de copia en escritura.

Pero si, por supuesto, también puedes hacer eso en C, es solo un poco más de trabajo. Cuando desea asignar sus datos a un nuevo bloque de datos, no hace una copia, sino que simplemente copia un puntero en una estructura de envoltura alrededor de sus datos. Debe realizar un seguimiento en sus bloques de datos del estado de los datos. Si ahora cambia algo en su nuevo bloque de datos, hace una copia “real” y cambia el estado. Por supuesto, ya no puede usar los operadores simples como “=” para la asignación, sino que necesita tener funciones (en C ++, solo haría una sobrecarga de operadores).

Una implementación más robusta debería usar contadores de referencia en lugar de una bandera simple, se lo dejo a usted.

Un ejemplo rápido y sucio: si tienes un

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

tiene que implementar funciones de asignación y getters/setters usted mismo.

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

También necesita escribir constructores y destructores. Realmente recomiendo c++ para esto.

  • Eso no genera la semántica COW que quiero, si el sistema operativo lo hiciera, solo copiaría (al menos en Mac OS X) la página 4k que se cambió, dejando el resto de los datos (otros 10 o 100 MB) -estructura todavía VACA. Por supuesto, podría y he implementado lo que realmente quiero, pero sería bueno si pudiera hacer que el sistema operativo lo hiciera por mí.

    – Chris Jefferson

    15 de octubre de 2009 a las 10:29

  • Una versión más nueva del kernel de Linux puede hacerlo automáticamente, los kernels 2.6.32+ tienen un código para reemplazar páginas duplicadas con enlaces de copia en escritura. lwn.net/Artículos/353501 , sin embargo, el subsistema ksm no está muy maduro y hasta ahora funciona al revés: las páginas se escanean después de haberlas copiado y se reemplazan si son idénticas. Si desea que lo controle desde el espacio de usuario, puede consultar linux/mm/ksm.c y realizar los cambios necesarios.

    -Gunther Piez

    15/10/2009 a las 21:53

  • La solución publicada realmente no es “CoW” en absoluto, es una emulación de software de la misma que fuerza todas las operaciones de “escritura” a través de una capa de direccionamiento indirecto. Creo que Chris estaba pidiendo específicamente una solución a nivel de memoria usando el hardware MMU. Y FWIW: no necesita una nueva versión del kernel de Linux. BSD mmap() ha sido compatible con MAP_PRIVATE durante décadas; ha sido parte de POSIX desde el principio.

    –Andy Ross

    15 de octubre de 2009 a las 23:10

avatar de usuario
DevSolar

El mecanismo de copia en escritura empleado, por ejemplo, por fork() es una característica de la MMU (Unidad de administración de memoria), que maneja la paginación de memoria para el kernel. El acceso a la MMU es una operación privilegiada, es decir, no se puede realizar mediante una aplicación de espacio de usuario. Tampoco tengo conocimiento de ninguna API de copia en escritura exportada al espacio del usuario.

(Por otra parte, no soy exactamente un gurú en la API de Linux, por lo que otros podrían señalar llamadas de API relevantes que me he perdido).

Editar: Y he aquí, MSalters está a la altura de las circunstancias. 😉

Debería poder abrir su propia memoria a través de /proc/$PID/mem y luego mmap() la parte interesante con MAP_PRIVATE a algún otro lugar.

  • Esto no funcionará porque /proc…/mem no es compatible con mmap. Ver también aquí.

    – colotoxicosis

    27/09/2013 a las 14:51


avatar de usuario
abeja desvanecida

Aquí hay un ejemplo de trabajo:

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

No estoy seguro de si necesito habilitar la sección comentada, por seguridad.

  • Esto no funcionará porque /proc…/mem no es compatible con mmap. Ver también aquí.

    – colotoxicosis

    27/09/2013 a las 14:51


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad