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! ~
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.