Explicación detallada de las funciones y la memoria compartida de Linux (incluidos ejemplos)

El concepto de memoria compartida

La memoria compartida significa que múltiples procesos pueden mapear una memoria común de una sección de memoria en su propio espacio de proceso para realizar el intercambio y la transmisión de datos. Es un recurso a nivel de kernel y es la forma más rápida de comunicación entre todos los procesos.

En el entorno de shell, puede utilizar ipcs para ver el estado del IPC del sistema actual, como la computadora actual:

$ ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2260992    deeplearni 600        524288     2          dest
0x00000000 2490369    deeplearni 600        67108864   2          dest
0x00000000 163842     root       777        7680       2
0x00000000 196611     root       777        8294400    2
0x00000000 229380     root       777        4096       2
0x00000000 262149     root       777        8192       2
0x00000000 294918     root       777        12288      2
...省略
0x00000000 2064444    root       777        233472     2
0x00000000 2097213    root       777        237568     2
0x00000000 2129982    root       777        241664     2
0x00000000 2162751    root       777        245760     2
0x00000000 2654272    deeplearni 600        524288     2          dest
0x00000000 2687041    deeplearni 600        524288     2          dest
0x00000000 2719810    deeplearni 600        524288     2          dest
0x00000000 2752579    deeplearni 600        524288     2          dest
0x00000000 2981956    deeplearni 600        524288     2          dest
0x00000000 2949189    deeplearni 600        524288     2          dest
0x00000000 3014726    deeplearni 600        67108864   2          dest

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

Este comando usa parámetros adicionales para ver una IPC por separado: -m (memoria compartida), -q (cola de mensajes), -s (semáforo).

Dado que varios procesos tienen derechos de acceso a la misma área de memoria, el problema de sincronización entre cada proceso debe resolverse y puede controlarse con semáforos.

[Beneficios del artículo] Xiaobian recomienda su propio grupo de Linux C / C ++: ¡812855908! Organicé algunos libros de aprendizaje y materiales de video que creo que son mejores para compartir, ¡y puedes agregarlos si los necesitas! ~
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Para cada segmento de memoria compartida, el kernel mantiene una estructura de tipo shmid_ds:

// 摘自所用ubuntu18.04电脑中的/usr/include/i386-linux-gnu/bits/shm.h
struct shmid_ds
  {
    
    
    struct ipc_perm shm_perm;           /* operation permission struct */
    size_t shm_segsz;                   /* size of segment in bytes */
    __time_t shm_atime;                 /* time of last shmat() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved1;
#endif
    __time_t shm_dtime;                 /* time of last shmdt() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved2;
#endif
    __time_t shm_ctime;                 /* time of last change by shmctl() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved3;
#endif
    __pid_t shm_cpid;                   /* pid of creator */
    __pid_t shm_lpid;                   /* pid of last shmop */
    shmatt_t shm_nattch;                /* number of current attaches */
    __syscall_ulong_t __glibc_reserved4;
    __syscall_ulong_t __glibc_reserved5;
  };

Operaciones relacionadas de memoria compartida

Crear / abrir memoria compartida La función
shmget () es necesaria para crear memoria compartida. El prototipo es el siguiente:

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);

El ID de la memoria compartida se devuelve cuando la creación se realiza correctamente y se devuelve -1 cuando se produce el error.

La clave del parámetro es el valor clave de la memoria compartida, el tamaño del parámetro es el tamaño de la memoria compartida creada y la bandera del parámetro es el tipo de operación de la función que llama. La función de shmget () determinada por la clave de parámetro y el indicador de parámetro:

  • Cuando la clave es IPC_PRIVATE, se crea una nueva memoria compartida y el valor del indicador no es válido.
  • Cuando la clave no es IPC_PRIVATE, y el bit IPC_CREAT está establecido para la bandera, pero el bit IPC_EXCL no está establecido, si la clave es un valor de clave de memoria compartida existente en el kernel, se abrirá; de lo contrario, se abrirá una nueva memoria compartida. ser creado.
  • Cuando la clave no es IPC_PRIVATE y los bits IPC_CREAT e IPC_EXCL están configurados en la bandera, solo se realiza la operación de creación de memoria compartida. Si la clave es un valor de clave de memoria compartida existente en el kernel, se devuelve un error EEXIST.

Adjunto (mapeo) de
memoria compartida Después de crear una memoria compartida, si un proceso quiere usarla, necesita adjuntar esta área de memoria a su propio espacio de proceso (o mapeo de direcciones), lo que requiere la función shmat ():

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int *shmat(int shmid, const void *addr, int flag);

La operación exitosa devuelve el puntero de dirección al segmento de memoria compartida y se devuelve el error -1.

El parámetro shmid es el ID de la memoria compartida. El parámetro addr y la bandera del parámetro juntos indican el valor de la dirección que se va a introducir. Normalmente solo hay dos usos:

  • addr es 0, lo que indica que el núcleo puede determinar la primera posición referenciable
  • addr es distinto de cero y SHM_RND se especifica en flag, luego este párrafo se introduce en la posición apuntada por addr.

Después de que la función shmat () se ejecute con éxito, el valor del contador shm_nattch de la estructura shmid_ds del segmento de memoria compartida shmid se incrementará en 1.

Separación de la memoria compartida
Cuando un proceso ha agotado la memoria compartida, debe desconectarse de su espacio de proceso, utilizando la función shmdt ():

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(void *addr);

Devuelve 0 si se ejecuta correctamente y -1 si falla.

El parámetro addr es el valor de retorno de llamar a shmat (), que es el puntero de dirección del segmento de memoria compartida. Una vez que la función shmdt () se ejecuta correctamente, el valor del contador shm_nattch se reduce en 1.

Control de memoria compartida
Utilice shmctl () para realizar una variedad de operaciones de control en el segmento de memoria compartida, el prototipo de función:

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_s *buf);

Devuelve 0 si se ejecuta correctamente y -1 si falla.

El parámetro shmid es el ID de la memoria compartida y el parámetro cmd indica la operación a realizar. Se usa junto con el parámetro * buf :

Tome la estructura shmid_ds de la memoria compartida apuntada por shmid y asigne un valor a la estructura apuntada por el parámetro buf

Ejemplo de programación

Los pasos básicos:

  • Generar clave, ftok ()
  • Use la tecla para crear / obtener una memoria compartida, shmget ()
  • Asignar memoria compartida, obtener dirección virtual, shmat ()
  • Usar memoria compartida a través del puntero de dirección
  • Eliminar la asignación, shmdt ()
  • Destruye la memoria compartida, shmctl ()

Ejemplo 1 El
proceso 1 crea memoria compartida y escribe datos, shm1.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    
    
    // generate key
    key_t key = ftok("./", 200);
    printf("key=%#x\n", key);

    // create a share memory
    int shmid = shmget(key, 8, IPC_CREAT|0666|IPC_EXCL);
    if(shmid == -1)
    {
    
    
        perror("shmget failed\n");
        exit(1);
    }
    printf("shmid=%#x\n", shmid);

    // map share memory to get the virtual address
    void *p = shmat(shmid, 0, 0);
    if((void *)-1 == p)
    {
    
    
        perror("shmat failed");
        exit(2);
    }

    // write data to share memory
    int *pi = p;
    *pi = 0xaaaaaaaa;
    *(pi+1) = 0x55555555;

    // remove the map
    if(shmdt(p) == -1)
    {
    
    
        perror("shmdt failed");
        exit(3);
    }

    // delete the share memory
    printf("use Enter to destroy the share memory\n");
    getchar();
    if(shmctl(shmid, IPC_RMID, NULL) == -1)
    {
    
    
        perror("shmctl");
        exit(4);
    }

    return 0;
}

El proceso 2 lee el contenido de la memoria compartida, shm2.c;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    
    
    // generate key
    key_t key = ftok("./", 200);
    printf("key=%#x\n", key);

    // get the share memory
    int shmid = shmget(key, 0, 0);
    if(shmid == -1)
    {
    
    
        perror("shmget failed\n");
        exit(1);
    }
    printf("shmid=%#x\n", shmid);

    // map share memory to get the virtual address
    void *p = shmat(shmid, 0, 0);
    if((void *)-1 == p)
    {
    
    
        perror("shmat failed");
        exit(2);
    }

    // read: get data from the share memory
    int x = *((int *)p);
    int y = *((int *)p+1);
    printf("x=%#x y=%#x\n", x, y);

    // remove the map
    if(shmdt(p) == -1)
    {
    
    
        perror("shmdt failed");
        exit(3);
    }

    return 0;
}

Compile y ejecute la prueba, primero ejecute el programa shm1 en un shell:

$ ./shm1
key=0xc81102ed
shmid=0x2e8047
use Enter to destroy the share memory

Ejecute el programa shm2 primero en otro shell:

$ ./shm2
key=0xc81102ed
shmid=0x2e8047
x=0xaaaaaaaa y=0x55555555

Puedes ver que la comunicación es exitosa. Presiona "Enter" en el primer shell para destruir la memoria compartida.

Ejemplo 2 El
ejemplo 1 usa la clave generada por la función ftok () para crear memoria compartida Este ejemplo usa el parámetro IPC_PRIVATE para crear memoria compartida.

Cree una memoria compartida y genere su número de identificación, create_shm.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSZ 4096

int main(void)
{
    
    
    int shm_id;

    shm_id = shmget(IPC_PRIVATE, BUFSZ, 0666);
    if(shm_id < 0)
    {
    
    
        printf("shmget failed!\n");
        exit(1);
    }
    printf("create a shared memory segment successfully: %d\n", shm_id);

    system("ipcs -m");

    exit(0);
}

Abra la memoria compartida con el ID especificado, escriba el contenido, write_shm.c:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    
    
   char name[4];
   int age;
}people;

int main(int argc, char **argv)
{
    
    
   int shm_id, i;
   char temp;
   people *p_map;

   if(argc != 2)
   {
    
    
       printf("USAGE:atshm <identifier>\n");
       exit(1);
   }

   // get share memory ID from input
   shm_id = atoi(argv[1]);// str to int

   // map the share memory to get the virtul address
   p_map = (people *)shmat(shm_id, NULL, 0);

   // write
   temp = 'a';
   for(i=0; i<10; i++)
   {
    
    
       temp+=1;
       memcpy((*(p_map+i)).name, &temp, 1);
       (*(p_map+i)).age=20+i;
   }

   // remove map
   if(shmdt(p_map)==-1)
       perror("detach error!\n");

   return 0;
}

Abra la memoria compartida con el ID especificado, lea el contenido, read_shm.c:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    
    
   char name[4];
   int age;
}people;

int main(int argc, char** argv)
{
    
    
   int shm_id, i;
   people *p_map;

   if(argc != 2)
   {
    
    
       printf("USAGC: atshm <identifier>\n");
       exit(1);
   }

   // get the share memory ID from input
   shm_id = atoi(argv[1]);// str to int

   // map the share memory to get the virtual address
   p_map = (people*)shmat(shm_id, NULL, 0);

   // read data
   for(i=0; i<10; i++)
   {
    
    
       printf("name:%s   ", (*(p_map+i)).name);
       printf("age %d\n", (*(p_map+i)).age);
   }

   // remove the map
   if(shmdt(p_map)==-1)
       perror("detach error!\n");

   return 0;
}

Para compilar los tres programas anteriores, primero ejecute el programa create_shm para crear una memoria compartida:

$ ./create_shm
create a shared memory segment successfully: 3080264

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2260992    deeplearni 600        524288     2          dest
0x00000000 2490369    deeplearni 600        67108864   2          dest
0x00000000 163842     root       777        7680       2
0x00000000 196611     root       777        8294400    2
0x00000000 229380     root       777        4096       2
...省略
0x00000000 2031675    root       777        229376     2
0x00000000 2064444    root       777        233472     2
0x00000000 2097213    root       777        237568     2
0x00000000 2129982    root       777        241664     2
0x00000000 2162751    root       777        245760     2
0x00000000 2654272    deeplearni 600        524288     2          dest
0x00000000 2687041    deeplearni 600        524288     2          dest
0x00000000 2719810    deeplearni 600        524288     2          dest
0x00000000 2752579    deeplearni 600        524288     2          dest
0x00000000 2981956    deeplearni 600        524288     2          dest
0x00000000 2949189    deeplearni 600        524288     2          dest
0x00000000 3014726    deeplearni 600        67108864   2          dest
0xc81102ed 3047495    deeplearni 666        8          0
0x00000000 3080264    deeplearni 666        4096       0

Puede ver que el programa creó una memoria compartida con shmid de 3080264, y luego puede ejecutar el programa write_shm, debe tomar el número de identificación como parámetro:

$ ./write_shm 3080264

Aunque no hay salida, el programa ha escrito el contenido en la memoria compartida y la memoria compartida no se ha eliminado. Puede ejecutar el programa read_shm para leer el contenido de la memoria compartida:

$ ./read_shm 3080264
name:b   age 20
name:c   age 21
name:d   age 22
name:e   age 23
name:f   age 24
name:g   age 25
name:h   age 26
name:i   age 27
name:j   age 28
name:k   age 29

Como puede ver, el programa lee 10 caracteres almacenados en la memoria compartida.

Además, la memoria compartida inútil también se puede eliminar a través de comandos, utilizando la forma de ipcrm -m ID de memoria compartida, como:

$ ipcrm -m 3080264

En este punto, use el comando ipcs -m para verificar que no haya memoria compartida con shmid de 3080264.

Supongo que te gusta

Origin blog.csdn.net/qq_40989769/article/details/110531512
Recomendado
Clasificación