HOWTO · C
Use la función mmap para escribir en la memoria en C
Este artículo educa sobre la función mmap() y muestra cómo podemos usar la función para escribir en la memoria en C.
En esta página
mmap() crea un mapeo de memoria en un proceso. Puedes usar un mapeo anónimo con permisos de escritura como memoria para datos de C, o un mapeo anónimo compartido para transmitir cambios entre procesos relacionados. Esta guía explica los seis argumentos, cómo detectar errores al crear el mapeo y cómo liberarlo con munmap().
Qué hace mmap() y qué devuelve
Un mapeo conecta un rango del espacio de direcciones virtuales de un proceso con un archivo, un dispositivo o memoria anónima. La memoria anónima no está respaldada por un archivo, por lo que sus cambios no se guardan en uno. Incluye <sys/mman.h> para usar mmap() y sus constantes relacionadas.
La función recibe seis argumentos y, si tiene éxito, devuelve la dirección del nuevo mapeo. Si falla, devuelve MAP_FAILED; compárala con ese valor centinela en lugar de comprobar si es NULL o cero. La llamada también puede establecer errno, lo que puede ayudar a diagnosticar el fallo.
Comprender los parámetros y las banderas de mmap()
Los argumentos, en este orden, son address, length, prot, flags, fd y offset:
addresses una dirección inicial sugerida. Si pasasNULL, el núcleo elige una dirección adecuada; las aplicaciones suelen hacerlo así.lengthes la cantidad de bytes que se asignarán al mapeo. El núcleo se encarga de la alineación de páginas del rango mapeado, pero usa de forma coherente la longitud solicitada al liberarlo.protestablece los permisos de acceso. CombinaPROT_READyPROT_WRITEpara los datos que debas leer y modificar. AñadePROT_EXECsolo si necesitas memoria ejecutable.flagsselecciona el comportamiento de compartición y respaldo.MAP_PRIVATEmantiene las actualizaciones como privadas del proceso;MAP_SHAREDhace que sean visibles a través del mapeo compartido.MAP_ANONYMOUScrea memoria sin respaldo de archivo. Estas opciones suelen combinarse con una de las banderas de compartición.fdidentifica un archivo para un mapeo respaldado por archivo. En sistemas Unix-like comunes, se ignora el descriptor cuando se usaMAP_ANONYMOUS; los ejemplos suelen pasar-1.offsetselecciona el byte inicial de un archivo y, para los mapeos respaldados por archivo, debe ser múltiplo del tamaño de página del sistema. Para un mapeo anónimo, usa cero.
Evita MAP_FIXED salvo que tengas una necesidad concreta y cuidadosamente controlada de ocupar una dirección: puede reemplazar los mapeos existentes en el rango solicitado. Muchos sistemas Unix-like ofrecen estas banderas y fork(), pero no están disponibles en todos los entornos de C.
Escribir en memoria anónima con mmap()
Este ejemplo completo mapea espacio para cinco enteros, escribe y lee sus valores y luego libera exactamente el rango solicitado. Presupone un sistema Unix-like que proporciona MAP_ANONYMOUS. Cuando se ejecuta correctamente, muestra la salida que aparece a continuación.
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
#include <stdio.h>
#include <sys/mman.h>
int main(void) {
const size_t count = 5;
const size_t length = count * sizeof(int);
int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (values == MAP_FAILED) {
perror("mmap");
return 1;
}
for (size_t i = 0; i < count; ++i) {
values[i] = (int)(i * 10);
}
for (size_t i = 0; i < count; ++i) {
printf("[%d] ", values[i]);
}
putchar('\n');
if (munmap(values, length) == -1) {
perror("munmap");
return 1;
}
return 0;
}
[0] [10] [20] [30] [40]
El mapeo permite escrituras porque sus permisos combinan PROT_READ y PROT_WRITE. MAP_PRIVATE es adecuado en este caso porque ningún otro proceso necesita ver las escrituras. munmap() debe recibir la dirección del mapeo y la misma longitud solicitada; no recibe la cantidad de elementos.
Compartir un mapeo anónimo entre procesos
Un mapeo creado con MAP_SHARED puede transmitir cambios a través de fork(). Así, el proceso hijo y el padre acceden a la misma región mapeada. El ejemplo vacía el búfer de la línea inicial antes de fork() para que no se duplique cuando la salida se redirige. El padre espera a que termine el hijo antes de leer sus actualizaciones. Al igual que el ejemplo anterior, este presupone compatibilidad con MAP_ANONYMOUS y fork() en un sistema Unix-like. La ejecución proporcionada produjo la salida que aparece a continuación. Las etiquetas visibles «Initial values» y «Updated values» permanecen en inglés en el código y la salida.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
const size_t count = 5;
const size_t length = count * sizeof(int);
int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (values == MAP_FAILED) {
perror("mmap");
return 1;
}
for (size_t i = 0; i < count; ++i) {
values[i] = (int)(i + 7);
}
printf("Initial values:");
for (size_t i = 0; i < count; ++i) {
printf(" %d", values[i]);
}
putchar('\n');
fflush(stdout);
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
munmap(values, length);
return 1;
}
if (child_pid == 0) {
for (size_t i = 0; i < count; ++i) {
values[i] *= 5;
}
if (munmap(values, length) == -1) {
_exit(1);
}
_exit(0);
}
int status;
if (waitpid(child_pid, &status, 0) == -1) {
perror("waitpid");
munmap(values, length);
return 1;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
fputs("child process failed\n", stderr);
munmap(values, length);
return 1;
}
printf("Updated values:");
for (size_t i = 0; i < count; ++i) {
printf(" %d", values[i]);
}
putchar('\n');
if (munmap(values, length) == -1) {
perror("munmap");
return 1;
}
return 0;
}
Initial values: 7 8 9 10 11
Updated values: 35 40 45 50 55
El hijo escribe a través del mapeo compartido y termina; cuando concluye waitpid(), el padre lee esos mismos cambios. Un mapeo MAP_PRIVATE no permitiría compartir los cambios de esta manera. Ambos procesos liberan su propio mapeo antes de terminar. En los mapeos respaldados por archivos, MAP_SHARED puede hacer visibles las actualizaciones a través del mapeo, pero guardarlas en el archivo requiere tener en cuenta aspectos adicionales de sincronización y del sistema de archivos.