Utilice shmget para asignar memoria compartida en C

Jinku Hu 12 octubre 2023
Utilice shmget para asignar memoria compartida en C

Este artículo demostrará varios métodos sobre el uso de la función shmget para asignar memoria compartida en C.

Utilice shmget para asignar memoria compartida en C

La memoria compartida es una de las formas de comunicación entre procesos que permite que dos o más procesos intercambien datos y se comuniquen rápidamente en el espacio del usuario. La memoria compartida implica que varios procesos comparten la misma región en la memoria y pueden modificar/acceder a este segmento según sea necesario.

La interfaz que demostraremos en los siguientes ejemplos se denomina memoria compartida System V, que se proporciona mediante cuatro funciones: shmget, shmat, shmdt y shmctl.

  • shmget se utiliza para crear un nuevo segmento de memoria compartida o recuperar un identificador para el segmento de memoria ya creado.
  • La llamada shmat se utiliza para adjuntar el segmento de memoria compartida dado al espacio de memoria del proceso de llamada.
  • shmdt se puede utilizar para separar el segmento de memoria dado
  • Se utiliza shmctl para modificar y desasignar el segmento con múltiples opciones.

El siguiente código de ejemplo implementa un uso básico de las funciones shmget y shmat para un proceso que crea un nuevo segmento compartido y luego escribe algunos textos en él. La función shmget toma tres argumentos, el primero de los cuales es la clave del segmento de memoria. El valor de la clave puede ser la macro IPC_PRIVATE si es necesario crear el nuevo segmento o el valor de la clave del segmento existente cuando se debe obtener el identificador de memoria con la llamada. El segundo argumento de shmget especifica el tamaño del segmento y el tercer argumento son los indicadores de permiso que pueden ser OR-ed para incluir múltiples valores.

Una vez creado el segmento de memoria, se obtiene el identificador de segmento y se puede pasar a la función shmat para adjuntar la memoria dada. El usuario puede pasar la dirección específica donde adjuntar el segmento de memoria dado como segundo argumento a shmat. Aún así, por lo general, se prefiere dejar que el kernel elija la dirección y especifique NULL para indicar esto.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>

enum { SEGMENT_SIZE = 0x6400 };

const char *data = "Hello there!";

int main(int argc, char *argv[]) {
  int status;
  int segment_id;

  segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE,
                      IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
  char *sh_mem = (char *)shmat(segment_id, NULL, 0);

  printf("Segment ID %d\n", segment_id);
  printf("Attached at %p\n", sh_mem);
  memmove(sh_mem, data, strlen(data) + 1);
  printf("%s\n", sh_mem);

  shmdt(sh_mem);
  shmctl(segment_id, IPC_RMID, 0);
  exit(EXIT_SUCCESS);
}

Producción :

Segment ID 1540195
Attached at 0x7f6ba1437000
Hello there!

Una vez que la función shmat devuelve el puntero válido, podemos tratarlo como cualquier dirección de memoria y operar en él según sea necesario. Finalmente, las funciones shmdt y shmctl son llamadas para separar el segmento dado y desasignar la memoria. Suponga que no se llama a shmctl en los segmentos de memoria asignados. En ese caso, permanecen en la memoria del sistema después de que finaliza el programa, y puede alcanzar el límite de todo el sistema en el segmento total de memoria compartida.

Por otro lado, el siguiente código de muestra demuestra cómo dos procesos pueden interactuar usando la memoria compartida. El código es el mismo que en el ejemplo anterior, excepto que después de imprimir la cadena "Hello there!", el proceso principal se bifurca y se crea un hijo, que almacena una cadena diferente en la misma dirección. Mientras tanto, el proceso padre se suspende, espera a que el hijo termine y finaliza con el código de éxito; la cadena recién almacenada se imprime en la consola. Si varios procesos necesitan modificar y acceder a segmentos de memoria compartida al mismo tiempo, el programador necesita emplear algunas herramientas de sincronización como semáforos.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>

enum { SEGMENT_SIZE = 0x6400 };

const char *data = "Hello there!";

int main(int argc, char *argv[]) {
  int status;
  int segment_id;

  segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE,
                      IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
  char *sh_mem = (char *)shmat(segment_id, 0, 0);

  printf("Segment ID %d\n", segment_id);
  printf("Attached at %p\n", sh_mem);
  memmove(sh_mem, data, strlen(data) + 1);
  printf("%s\n", sh_mem);

  pid_t child_pid = fork();
  if (child_pid == -1) perror("fork");

  if (child_pid == 0) {
    strcpy(sh_mem, "NEW DATA Stored by Child Process\0");

    printf("child pid - %d\n", getpid());
    exit(EXIT_SUCCESS);
  } else {
    pid_t ret = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
    if (ret == -1) perror("waitpid");

    if (WIFEXITED(status))
      printf("Child exited, status - %d\n", WEXITSTATUS(status));

    if (WEXITSTATUS(status) == 0) printf("%s\n", sh_mem);
  }

  shmdt(sh_mem);
  shmctl(segment_id, IPC_RMID, 0);
  exit(EXIT_SUCCESS);
}

Producción :

Segment ID 1540195
Attached at 0x7fd825a25000
Hello there!
child pid - 27291
Child exited, status - 0
NEW DATA Stored by Child Process
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Artículo relacionado - C Memory