¿Puedo hacer una memcpy copy-on-write en Linux?

Tengo un código donde copio con frecuencia un gran bloque de memoria, a menudo después de hacer solo pequeños cambios en él.

Implementé un sistema que rastrea los cambios, pero pensé que sería bueno, si era posible, decirle al sistema operativo que hiciera una “copia de escritura” de la memoria, y dejar que se ocupe solo de hacer una copia de esas partes. que cambio Sin embargo, mientras que Linux hace copy-on-write, por ejemplo cuando fork () ing, no puedo encontrar una manera de controlarlo y hacerlo yo mismo.

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

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

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 copy-on-write.

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

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

Un ejemplo rápido y sucio: si tienes un

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

debe 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 debe escribir constructores y destructores. Realmente recomiendo c ++ para esto.

El mecanismo de copiado en escritura empleado, por ejemplo, mediante fork () es una característica de la MMU (Unidad de gestión de memoria), que maneja la búsqueda de memoria para el kernel. El acceso a la MMU es una operación privilegiada, es decir, no puede ser realizada por una aplicación de espacio de usuario. Tampoco conozco ninguna API de copia en escritura exportada al espacio de usuario.

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

Editar: Y lo, MSalters se levanta a la ocasión. 😉

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

Aquí hay un ejemplo de trabajo:

 #include  #include  #include  #include  #include  #include  #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, para mayor seguridad.