C / C ++ usa la función mmap / munmap para asignar memoria

Hay muchos métodos comunes de administración y asignación de memoria en C / C ++, como punteros inteligentes, contenedores STL, nuevo / eliminar, malloc / free, brk, sbrk, etc. Linux tiene un método de administración de memoria de nivel relativamente bajo mmap / munmap, que requiere Mantiene la memoria virtual asignada completamente por sí mismo, sin ninguna otra estructura de datos auxiliar para ayudar a mantener el espacio de la memoria. La llamada al sistema mmap puede asignar un área de memoria virtual anónima o asignar un archivo a la memoria. Esta asignación realiza operaciones de archivo como operaciones de memoria directa. Este método se denomina asignación de memoria.

 mmap () debe asignarse en páginas de memoria (PAGE_SIZE), y la memoria solo se puede asignar en páginas. Si desea asignar un rango de direcciones que no sea un múltiplo entero de PAGE_SIZE, primero debe realizar la alineación de la memoria y forzar el tamaño a ser un múltiplo de PAGE_SIZE Mapearlo.
 La operación mmap proporciona un mecanismo para que los programas de usuario accedan directamente a la memoria del dispositivo. Este mecanismo es más eficiente que copiar datos en el espacio del usuario y el espacio del kernel. Se usa comúnmente en aplicaciones que requieren alto rendimiento, pero mmap no se puede realizar en dispositivos orientados a la transmisión La implementación de mmap está relacionada con el hardware.

1. Ingrese man mmap en la terminal para ver la documentación API de esta función. La descripción específica de esta función es la siguiente:

#include <sys / mman.h>

 void * mmap (void * start, size_t length, int prot, int flags, int fd, off_t offset);
 int munmap (void * start, size_t length);
Parámetros de función:
start: apunta a la dirección de inicio de la memoria a ser mapeado, generalmente establecido en NULL, lo que significa dejar que el sistema seleccione automáticamente la dirección y devuelva la dirección después de que el mapeo sea exitoso.
longitud: representa la cantidad de parte del archivo que se asigna a la memoria, es decir, la longitud del área de asignación.
prot: El modo de protección del área mapeada, que no puede entrar en conflicto con el modo abierto del archivo. Puede ser de las siguientes formas:
                    El área de mapeo PROT_EXEC se puede ejecutar.
                    El área de mapeo PROT_READ se puede leer.
                    El área de mapeo PROT_WRITE se puede escribir
                    . No se puede acceder al área de mapeo PROT_NONE.

Los métodos anteriores se pueden combinar razonablemente con la operación o ("|").
Banderas: afectan varias características del área de mapeo, especifican el tipo de objeto de mapeo, las opciones de mapeo y si la página de mapeo se puede compartir. Debe especificar MAP_SHARED o MAP_PRIVATE al llamar a mmap (). El valor de las banderas puede ser una combinación de uno o más de los siguientes bits:
                    MAP_FIXED Si la dirección apuntada por el parámetro start no puede mapearse correctamente, la asignación se abandonará y la dirección no se modificará. Generalmente se desaconseja el uso de esta bandera.
                    MAP_SHARED copiará los datos escritos en el área mapeada nuevamente en el archivo y permitirá que otros procesos que mapean el archivo se compartan. Comparta el espacio de mapeo con todos los demás procesos que mapean este objeto. Escribir en el área compartida equivale a enviar a un archivo. Hasta que se llame a msync () o munmap (), el archivo no se actualizará realmente.
                    La operación de escritura de MAP_PRIVATE en el área mapeada producirá una copia del archivo mapeado, es decir, cualquier modificación realizada en esta área mediante una "copia al escribir" privada no volverá a escribir el contenido del archivo original y afectará al archivo original. Esta bandera y las banderas anteriores son mutuamente excluyentes, solo se puede usar una de ellas.
                    MAP_ANONYMOUS establece un mapeo anónimo. En este momento, el parámetro fd se ignora, no hay archivos involucrados y el área de mapeo no se puede compartir con otros procesos.
                    MAP_DENYWRITE solo permite operaciones de escritura en el área asignada, y se rechazarán otras operaciones de escritura directa en el archivo.
                    MAP_LOCKED bloquea el área mapeada, lo que significa que el área no se intercambiará, evitando así que las páginas se intercambien sin memoria.

                   MAP_NORESERVE // No reserve espacio de intercambio para este mapeo. Cuando se reserva el espacio de intercambio, se puede garantizar la modificación del área de mapeo. Cuando el espacio de intercambio no está reservado y la memoria es insuficiente, la modificación del área de mapeo provocará una señal de violación de segmento.
                  MAP_LOCKED // Bloquea las páginas en el área de mapeo para evitar que las páginas se salgan de la memoria.
                  MAP_GROWSDOWN // Se usa para que la pila le diga al sistema VM del kernel que el área de mapeo se puede expandir hacia abajo.

                  MAP_ANON // Otro nombre para MAP_ANONYMOUS, que ya no se usa.
                  MAP_FILE // Indicador compatible, ignorado.
                  MAP_32BIT // Coloque el área de mapeo en los 2GB inferiores del espacio de direcciones del proceso, se ignorará cuando se especifique MAP_FIXED. Actualmente, esta bandera solo es compatible con plataformas x86-64.
                  MAP_POPULATE // Prepare la tabla de páginas mediante lectura previa para la asignación de archivos. El acceso posterior al área mapeada no será bloqueado por violaciones de página.
                  MAP_NONBLOCK // Solo tiene sentido cuando se usa con MAP_POPULATE. No se realiza una lectura previa y solo se crean entradas en la tabla de páginas para las páginas que ya existen en la memoria.
fd: el descriptor de archivo que se asignará a la memoria. Si se utiliza la asignación de memoria anónima, es decir, MAP_ANONYMOUS se establece en banderas y fd se establece en -1. Algunos sistemas no admiten la asignación de memoria anónima, puede usar fopen para abrir el archivo / dev / zero
          y luego asignar el archivo, también puede lograr el efecto de la asignación de memoria anónima.
offset: el offset del mapeo del archivo. Generalmente se establece en 0, lo que significa que corresponde al principio del archivo. El offset debe ser un múltiplo entero de PAGE_SIZE.

valor de retorno:

       Tras una ejecución exitosa, mmap () devuelve la dirección de inicio de la memoria del área mapeada si el mapeo es exitoso, y munmap () devuelve 0. Cuando falla, mmap () devuelve MAP_FAILED [su valor es (void *) - 1], la causa del error se almacena en errno y munmap devuelve -1.

Código de error: el
            parámetro EBADF fd no es un descriptor de archivo válido
            . El permiso de acceso EACCES es incorrecto. Si es MAP_PRIVATE, el archivo debe ser legible. Si se usa MAP_SHARED, PROT_WRITE y el archivo deben poder escribirse.
            EINVAL Uno de los parámetros start, length o offset es ilegal.
            El archivo EAGAIN está bloqueado o hay demasiada memoria bloqueada.
            ENOMEM no tiene memoria.

errno valor de retorno:
 errno se establece en uno de los siguientes valores:
            EACCES: error de acceso
            EAGAIN: el archivo está bloqueado o hay demasiada memoria bloqueada
            EBADF: fd no es un descriptor de archivo válido
            EINVAL: uno o más parámetros no son válidos
            ENFILE: Se alcanzó el límite del sistema para abrir archivos.
            ENODEV: El sistema de archivos donde se encuentra el archivo especificado no admite la asignación de memoria.
            ENOMEM: Memoria insuficiente o el proceso ha superado el número máximo de asignaciones de memoria.
            EPERM: Energía insuficiente, funcionamiento no permitido
            ETXTBSY: Abra el archivo escrito y especifique el indicador MAP_DENYWRITE
            SIGSEGV: Intente escribir en el área de solo lectura.
            SIGBUS: Intente acceder al área de memoria que no pertenece al proceso. La
llamada a la capa de usuario es muy simple. La función específica es mapear directamente la memoria física a la memoria virtual del usuario El espacio del usuario puede manipular directamente el espacio físico. Pero para la capa del kernel, su implementación específica es más complicada.

Llamada al sistema: la llamada al sistema
 mmap () permite que los procesos compartan memoria mediante la asignación del mismo archivo común. Una vez que el archivo ordinario se asigna al espacio de direcciones del proceso, el proceso puede acceder al archivo como la memoria ordinaria sin llamar a read (), write () y otras operaciones.
 Nota: De hecho, la llamada al sistema mmap () no está diseñada únicamente para su uso en memoria compartida. En sí mismo, proporciona una forma diferente de acceder a archivos ordinarios. El proceso puede operar en archivos ordinarios como la memoria de lectura y escritura.
     La memoria compartida IPC de Posix o SystemV es puramente para compartir, por supuesto, mmap () es también una de sus principales aplicaciones.
 
 El sistema llama a mmap () de dos formas para compartir memoria:
  (1) Utilice la asignación de memoria proporcionada por archivos ordinarios: adecuada para cualquier proceso; en este momento, debe abrir o crear un archivo y luego llamar a mmap (); típico El código de llamada es el siguiente:
   fd = open (nombre, bandera, modo);
   if (fd <0)
   ...
   ptr = mmap (NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   realizar memoria compartida a través de mmap () Hay muchas características y puntos a los que prestar atención en el método de comunicación, explicaremos en detalle en el ejemplo.
  (2) Utilice archivos especiales para proporcionar un mapeo de memoria anónimo: adecuado para procesos con parentesco; debido al parentesco especial entre los procesos padre e hijo, primero se llama a mmap () en el proceso padre y luego a fork ().
       Luego, después de llamar a fork (), el proceso hijo hereda el espacio de direcciones mapeado de forma anónima por el proceso padre y también hereda la dirección devuelta por mmap (), de modo que los procesos padre e hijo pueden comunicarse a través del área mapeada.
       Tenga en cuenta que esta no es una relación de herencia general. En términos generales, el proceso hijo mantiene por separado algunas variables heredadas del proceso padre. La dirección devuelta por mmap () es mantenida por los procesos padre e hijo.
       La mejor forma de implementar la memoria compartida para los procesos relacionados es utilizar la asignación de memoria anónima. En este momento, no es necesario que especifique un archivo específico, solo configure la bandera correspondiente.

 int munmap (void * addr, size_t len)
 Esta llamada libera una relación de mapeo en el espacio de direcciones del proceso, addr es la dirección devuelta cuando se llama a mmap () y len es el tamaño del área de mapeo. Cuando se libera la relación de mapeo, el acceso a la dirección de mapeo original provocará una falla de segmentación.

 int msync (void * addr, size_t len, int flags)
 En términos generales, los cambios de proceso en el contenido compartido en el espacio de mapeo no se escriben directamente en el archivo de disco y la operación generalmente se realiza después de llamar a munmap (). El contenido del archivo en el disco puede ser coherente con el contenido del área de memoria compartida llamando a msync ().

Ejemplo de programa:
 A continuación se dan dos ejemplos de uso de mmap (). El ejemplo 1 muestra que dos procesos realizan la comunicación de memoria compartida al mapear archivos ordinarios, y el ejemplo 2 muestra que los procesos padre e hijo realizan la memoria compartida a través de mapeo anónimo.
 La llamada al sistema mmap () tiene muchos puntos interesantes. El siguiente es un ejemplo de comunicación entre procesos a través de mmap () mapeando archivos ordinarios. Usamos este ejemplo para ilustrar las características y precauciones de mmap () para lograr memoria compartida.

 Ejemplo 1: Dos procesos realizan la comunicación de memoria compartida mediante la asignación de archivos comunes. El
 Ejemplo 1 incluye dos subrutinas: map_normalfile1.cy map_normalfile2.c. Compile dos programas, los archivos ejecutables son map_normalfile1 y map_normalfile2.
 Dos programas especifican el mismo archivo a través de parámetros de línea de comando para lograr la comunicación entre procesos en el modo de memoria compartida. map_normalfile2 intenta abrir un archivo normal especificado por el parámetro de la línea de comando, mapea el archivo al espacio de direcciones del proceso y escribe el espacio de direcciones mapeado.
 map_normalfile1 mapea el archivo especificado por el parámetro de línea de comando al espacio de direcciones del proceso y luego realiza una operación de lectura en el espacio de direcciones mapeado. De esta manera, dos procesos especifican el mismo archivo a través de parámetros de línea de comando para lograr la comunicación entre procesos en modo de memoria compartida.
 A continuación se muestran dos códigos de programa:

 /*-------------map_normalfile1.c-----------*/
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <fcntl.h>
  #include <unistd.h>
  
  typedef struct{
   char name[4];
   int age;
  }people;
  
  main(int argc, char** argv) // map a normal file as shared mem:
  {
   int fd,i;
   people *p_map;
   char temp;
   
   fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
   
   if(fd < 0)
   {
    printf("error open\n");
    exit(1);
   }
   
   lseek(fd,sizeof(people)*5-1,SEEK_SET);
   write(fd,"",1);
   p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
   close( fd );
   temp = 'a';
   for(i=0; i<10; i++)
   {
    temp += 1;
    memcpy( ( *(p_map+i) ).name, &temp,2 );
    ( *(p_map+i) ).age = 20+i;
   }
   
   printf(" initialize over \n ");
   sleep(10);
   munmap( p_map, sizeof(people)*10 );
   printf( "umap ok \n" );
  }

  

Ejemplo 2:

/*-------------map_normalfile2.c-----------*/
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <fcntl.h>
  #include <unistd.h>
  typedef struct{
   char name[4];
   int age;
  }people;

  main(int argc, char** argv) // map a normal file as shared mem:
  {
   int fd,i;
   people *p_map;
   fd=open( argv[1],O_CREAT|O_RDWR,00777 );
   p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
   for(i = 0;i<10;i++)
   {
    printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
   }
   munmap( p_map,sizeof(people)*10 );
  }
  

map_normalfile1.c primero define una estructura de datos de personas (la estructura de datos se usa aquí porque los datos en el área de memoria compartida suelen tener un formato fijo, que está determinado por cada proceso de comunicación. La estructura es generalmente representativa).
 map_normfile1 abre o crea un archivo y establece la longitud del archivo al tamaño de la estructura de 5 personas. Luego, a partir de la dirección de retorno de mmap (), se configuran estructuras de 10 personas.
 Luego, el proceso duerme durante 10 segundos, espera a que otros procesos asignen el mismo archivo y, finalmente, lo anule.
 map_normfile2.c simplemente mapea un archivo, lee la estructura de 10 personas de la dirección devuelta por mmap () en el formato de la estructura de datos de personas, genera el valor leído y luego desmapea.
  
 Después de compilar los dos programas en archivos ejecutables map_normalfile1 y map_normalfile2 respectivamente, ejecute ./map_normalfile2 / tmp / test_shm en una terminal, y la salida del programa es la siguiente:
  inicializar sobre
  umap ok
 Después de la salida de map_normalfile1 inicializar de nuevo, antes de generar umap ok, ejecutar map_normalfile2 / tmp / test_shm en otra terminal producirá la siguiente salida (para ahorrar espacio, la salida es un resultado ligeramente ordenado):
  nombre: b edad 20; nombre: c edad 21; nombre: d edad 22; nombre: e 23 años; nombre: f 24 años;
  nombre: g 25 años; nombre: h 26 años; nombre: tengo 27 años; nombre: j 28 años; nombre: k 29 años;
 Después de que map_normalfile1 genere umap ok, ejecutar map_normalfile2 generará los siguientes resultados:
  nombre: b edad 20; nombre: c edad 21; nombre: d edad 22; nombre: e edad 23; nombre: f edad 24;
  nombre: edad 0; nombre : edad 0; nombre: edad 0; nombre: edad 0; nombre: edad 0;
 Conclusiones que se pueden extraer de los resultados de ejecución del programa:
  1. La longitud del contenido del archivo mapeado final no excederá el tamaño inicial del archivo en sí. Es decir, la asignación no puede cambiar el tamaño del archivo;
  2. El tamaño efectivo del espacio de direcciones que se puede usar para la comunicación del proceso generalmente está limitado por el tamaño del archivo asignado, pero no completamente limitado por el tamaño del archivo.
     El archivo abierto se trunca al tamaño de 5 personas, y las estructuras de datos de 10 personas se inicializan en map_normalfile1. En el momento correcto (después de que la salida de map_normalfile1 se inicialice, antes de la salida de umap ok) al llamar a map_normalfile2
     se encontrará que la salida de map_normalfile2 las 10 personas El valor de la estructura se discutirá en detalle más adelante.
     Nota: En Linux, la protección de la memoria se basa en la página como unidad básica. Incluso si el archivo mapeado tiene sólo un byte de tamaño, el kernel asignará un tamaño de página de memoria para el mapeo.
         Cuando el archivo mapeado es más pequeño que el tamaño de una página, el proceso puede acceder a un tamaño de página comenzando desde la dirección de retorno mmap () sin error;
         Sin embargo, si se accede a un espacio de direcciones que no sea una página, se producirá un error, que se describirá más adelante. Por lo tanto, el tamaño efectivo del espacio de direcciones que se puede usar para la comunicación entre procesos no excede la suma del tamaño del archivo y el tamaño de una página.
  3. Una vez mapeado el archivo, el acceso a la dirección de retorno del proceso que llama a mmap () es un acceso a un área de memoria determinada, que está temporalmente fuera de la influencia del archivo en el disco. Todas las operaciones en el espacio de direcciones de retorno de mmap () solo son significativas en la memoria.
     Solo después de llamar a munmap () o msync (), el contenido correspondiente en la memoria se puede volver a escribir en el archivo de disco, y el contenido escrito aún no puede exceder el archivo del tamaño de.

Ejemplo de programa:

Ejemplo 1:

//
//  main.cpp
//
//
//  Created by ChengChao on 14-9-27.
//  Copyright (c) 2014年 cc. All rights reserved.
//
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, const char * argv[]) {

    //申请内存
    int* arr = static_cast<int*>(mmap(
                    NULL,                   //分配的首地址
                    getpagesize(),          //分配内存大小(必须是页的整数倍, 32位1页=4k)
                    PROT_READ | PROT_WRITE, //映射区域保护权限:读|写
                    MAP_ANON | MAP_SHARED,  //匿名映射(不涉及文件io), 后面两个参数忽略
                    0,                      //要映射到内存中的文件描述符
                    0                       //文件映射的偏移量,通常设置为0,必须是页的整数倍
                ));
    printf("申请内存大小=%dk\n", sizeof(arr));
    
    *arr = 10;
    *(arr + 1) = 20;
    *(arr + 2) = 30;
    
    printf("arr[2]=%d\n", arr[2]);
    
    //释放指针arr指向的内存区域,并制定释放的内存大小
    munmap(arr, getpagesize());
    
    return 0;
}

Ejemplo 2:

#include <fstream>
#include <iostream>
#include <cstdlib>
#include <sys/mman.h> 
#include <unistd.h>
#include <fcntl.h>
using namespace std;
 
int main(int argc, char* argv[])
{
    int fd=open("/mnt/e/file",O_RDWR);
    FILE* f=fdopen(fd,"rw");
    fseek(f,0,SEEK_END);
    long ps=ftell(f);
    cout << "file size: " << ps << endl;
	
    //分配的大小必须为页的整数倍
    int* p = (int*)mmap(NULL, ps, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
    {  
       perror("mmap error");  
       exit(1);  
    }
	
    //比如我用2个进程运行该程序,进程1 让*p=100 ,那么进程2 去读取*p也会变成100,但重要的是文件并没有更新,就是说没有IO操作,文件仅仅在msync()或mumap()调用时才会被更新,因为此时2个进程映射的该文件,都指向了相同的内存区域,也就说只有内存中的数据改变了
    int oper,data;
    while(true){
        cin>>oper;
        if(oper==1){
            cin>>data;
            *p=data;
        }
        else{
            cout<<*p<<endl;
        }
    }
	
//...
	
    munmap(p, ps);  
    close(fd);
    return 0;
}

https://www.xuebuyuan.com/1356242.html

http://www.bubuko.com/infodetail-384719.html

Supongo que te gusta

Origin blog.csdn.net/sunlin972913894/article/details/103963022
Recomendado
Clasificación