GNU/Linux >> Tutoriales Linux >  >> Linux

Comunicación entre procesos en Linux:Almacenamiento compartido

Este es el primer artículo de una serie sobre la comunicación entre procesos (IPC) en Linux. La serie utiliza ejemplos de código en C para aclarar los siguientes mecanismos de IPC:

  • Archivos compartidos
  • Memoria compartida (con semáforos)
  • Tubos (con y sin nombre)
  • Colas de mensajes
  • Enchufes
  • Señales

Este artículo revisa algunos conceptos básicos antes de pasar a los dos primeros de estos mecanismos:archivos compartidos y memoria compartida.

Conceptos básicos

Un proceso es un programa en ejecución, y cada proceso tiene su propio espacio de direcciones, que comprende las ubicaciones de memoria a las que el proceso puede acceder. Un proceso tiene uno o más hilos de ejecución, que son secuencias de instrucciones ejecutables:un single-threaded proceso tiene un solo subproceso, mientras que un multiproceso El proceso tiene más de un hilo. Los subprocesos dentro de un proceso comparten varios recursos, en particular, el espacio de direcciones. En consecuencia, los subprocesos dentro de un proceso pueden comunicarse directamente a través de la memoria compartida, aunque algunos lenguajes modernos (p. ej., Go) fomentan un enfoque más disciplinado, como el uso de canales seguros para subprocesos. De interés aquí es que diferentes procesos, por defecto, no compartir memoria.

Hay varias formas de iniciar procesos que luego se comunican, y dos formas dominan en los ejemplos que siguen:

  • Se usa una terminal para iniciar un proceso, y quizás se use una terminal diferente para iniciar otro.
  • La función del sistema bifurcación se llama dentro de un proceso (el padre) para generar otro proceso (el hijo).

Los primeros ejemplos toman el enfoque terminal. Los ejemplos de código están disponibles en un archivo ZIP en mi sitio web.

Archivos compartidos

Los programadores están muy familiarizados con el acceso a archivos, incluidas las muchas trampas (archivos inexistentes, permisos de archivos incorrectos, etc.) que acosan el uso de archivos en los programas. No obstante, los archivos compartidos pueden ser el mecanismo IPC más básico. Considere el caso relativamente simple en el que un proceso (productor ) crea y escribe en un archivo y otro proceso (consumidor ) lee de este mismo archivo:

         writes  +-----------+  reads
producer-------->| disk file |<-------consumer
                 +-----------+

El desafío obvio al usar este mecanismo de IPC es que una condición de carrera podría surgir:el productor y el consumidor podrían acceder al archivo exactamente al mismo tiempo, lo que haría que el resultado fuera indeterminado. Para evitar una condición de carrera, el archivo debe bloquearse de forma que se evite un conflicto entre una escritura operación y cualquier otra operación, ya sea una lectura o un escribir . La API de bloqueo en la biblioteca del sistema estándar se puede resumir de la siguiente manera:

  • Un productor debe obtener un bloqueo exclusivo en el archivo antes de escribirlo. Una exclusiva un proceso puede retener el bloqueo como máximo, lo que descarta una condición de carrera porque ningún otro proceso puede acceder al archivo hasta que se libere el bloqueo.
  • Un consumidor debe obtener al menos un bloqueo compartido en el archivo antes de leerlo. Múltiples lectores puede tener un compartido bloqueo al mismo tiempo, pero sin escritor puede acceder a un archivo cuando incluso un solo lector tiene un candado compartido.

Una cerradura compartida promueve la eficiencia. Si un proceso solo lee un archivo y no cambia su contenido, no hay razón para evitar que otros procesos hagan lo mismo. Escribir, sin embargo, exige claramente el acceso exclusivo a un archivo.

La biblioteca de E/S estándar incluye una función de utilidad llamada fcntl que se puede usar para inspeccionar y manipular bloqueos exclusivos y compartidos en un archivo. La función funciona a través de un descriptor de archivo , un valor entero no negativo que, dentro de un proceso, identifica un archivo. (Diferentes descriptores de archivo en diferentes procesos pueden identificar el mismo archivo físico). Para el bloqueo de archivos, Linux proporciona la función de biblioteca flock , que es un envoltorio delgado alrededor de fcntl . El primer ejemplo usa el fcntl función para exponer los detalles de la API.

Ejemplo 1. El productor programa

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FileName "data.dat"
#define DataString "Now is the winter of our discontent\nMade glorious summer by this sun of York\n"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  struct flock lock;
  lock.l_type = F_WRLCK;    /* read/write (exclusive versus shared) lock */
  lock.l_whence = SEEK_SET; /* base for seek offsets */
  lock.l_start = 0;         /* 1st byte in file */
  lock.l_len = 0;           /* 0 here means 'until EOF' */
  lock.l_pid = getpid();    /* process id */

  int fd; /* file descriptor to identify a file within a process */
  if ((fd = open(FileName, O_RDWR | O_CREAT, 0666)) < 0)  /* -1 signals an error */
    report_and_exit("open failed...");

  if (fcntl(fd, F_SETLK, &lock) < 0) /** F_SETLK doesn't block, F_SETLKW does **/
    report_and_exit("fcntl failed to get lock...");
  else {
    write(fd, DataString, strlen(DataString)); /* populate data file */
    fprintf(stderr, "Process %d has written to data file...\n", lock.l_pid);
  }

  /* Now release the lock explicitly. */
  lock.l_type = F_UNLCK;
  if (fcntl(fd, F_SETLK, &lock) < 0)
    report_and_exit("explicit unlocking failed...");

  close(fd); /* close the file: would unlock if needed */
  return 0;  /* terminating the process would unlock as well */
}

Los principales pasos en el productor El programa anterior se puede resumir de la siguiente manera:

  • El programa declara una variable de tipo struct flock , que representa un candado e inicializa los cinco campos de la estructura. La primera inicialización:
    lock.l_type = F_WRLCK; /* exclusive lock */

    hace que el bloqueo sea exclusivo (lectura-escritura ) en lugar de una compartida (de solo lectura ) cerrar con llave. Si el productor obtiene el bloqueo, ningún otro proceso podrá escribir o leer el archivo hasta que el productor libera el bloqueo, ya sea explícitamente con la llamada adecuada a fcntl o implícitamente cerrando el archivo. (Cuando finaliza el proceso, todos los archivos abiertos se cerrarán automáticamente, liberando así el bloqueo).

  • El programa luego inicializa los campos restantes. El efecto principal es que el todo el archivo debe ser bloqueado. Sin embargo, la API de bloqueo permite bloquear solo los bytes designados. Por ejemplo, si el archivo contiene varios registros de texto, se podría bloquear un solo registro (o incluso parte de un registro) y dejar el resto desbloqueado.
  • La primera llamada a fcntl :
    if (fcntl(fd, F_SETLK, &lock) < 0)

    intenta bloquear el archivo exclusivamente, verificando si la llamada tuvo éxito. En general, el fcntl la función devuelve -1 (por lo tanto, menor que cero) para indicar falla. El segundo argumento F_SETLK significa que la llamada a fcntl no bloque:la función regresa inmediatamente, ya sea otorgando el bloqueo o indicando falla. Si la bandera F_SETLKW (la W al final es para esperar ) en su lugar, la llamada a fcntl bloquearía hasta que fuera posible obtener la cerradura. En las llamadas a fcntl , el primer argumento fd es el descriptor del archivo, el segundo argumento especifica la acción a realizar (en este caso, F_SETLK para establecer el bloqueo), y el tercer argumento es la dirección de la estructura de bloqueo (en este caso, &lock ).

  • Si el productor obtiene el bloqueo, el programa escribe dos registros de texto en el archivo.
  • Después de escribir en el archivo, el productor cambia el l_type de la estructura de bloqueo campo para desbloquear valor:
    lock.l_type = F_UNLCK;

    y llama a fcntl para realizar la operación de desbloqueo. El programa termina cerrando el archivo y saliendo.

Ejemplo 2. El consumidor programa

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#define FileName "data.dat"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  struct flock lock;
  lock.l_type = F_WRLCK;    /* read/write (exclusive) lock */
  lock.l_whence = SEEK_SET; /* base for seek offsets */
  lock.l_start = 0;         /* 1st byte in file */
  lock.l_len = 0;           /* 0 here means 'until EOF' */
  lock.l_pid = getpid();    /* process id */

  int fd; /* file descriptor to identify a file within a process */
  if ((fd = open(FileName, O_RDONLY)) < 0)  /* -1 signals an error */
    report_and_exit("open to read failed...");

  /* If the file is write-locked, we can't continue. */
  fcntl(fd, F_GETLK, &lock); /* sets lock.l_type to F_UNLCK if no write lock */
  if (lock.l_type != F_UNLCK)
    report_and_exit("file is still write locked...");

  lock.l_type = F_RDLCK; /* prevents any writing during the reading */
  if (fcntl(fd, F_SETLK, &lock) < 0)
    report_and_exit("can't get a read-only lock...");

  /* Read the bytes (they happen to be ASCII codes) one at a time. */
  int c; /* buffer for read bytes */
  while (read(fd, &c, 1) > 0)    /* 0 signals EOF */
    write(STDOUT_FILENO, &c, 1); /* write one byte to the standard output */

  /* Release the lock explicitly. */
  lock.l_type = F_UNLCK;
  if (fcntl(fd, F_SETLK, &lock) < 0)
    report_and_exit("explicit unlocking failed...");

  close(fd);
  return 0;
}

El consumidor El programa es más complicado de lo necesario para resaltar las características de la API de bloqueo. En particular, el consumidor El programa primero verifica si el archivo está bloqueado exclusivamente y solo luego intenta obtener un bloqueo compartido. El código relevante es:

lock.l_type = F_WRLCK;
...
fcntl(fd, F_GETLK, &lock); /* sets lock.l_type to F_UNLCK if no write lock */
if (lock.l_type != F_UNLCK)
  report_and_exit("file is still write locked...");

El F_GETLK operación especificada en el fcntl llama a cheques para un bloqueo, en este caso, un bloqueo exclusivo dado como F_WRLCK en la primera afirmación anterior. Si el bloqueo especificado no existe, el fcntl la llamada cambia automáticamente el campo de tipo de bloqueo a F_UNLCK para indicar este hecho. Si el archivo está bloqueado exclusivamente, el consumidor termina (Una versión más robusta del programa podría tener el consumidor dormir un poco y vuelve a intentarlo varias veces).

Si el archivo no está bloqueado actualmente, entonces el consumidor intenta obtener un acceso compartido (de solo lectura) ) bloquear (F_RDLCK ). Para acortar el programa, el F_GETLK llamar a fcntl podría descartarse porque F_RDLCK la llamada fallaría si una lectura-escritura lock ya estaban retenidos por algún otro proceso. Recuerde que un solo lectura lock impide que cualquier otro proceso escriba en el archivo, pero permite que otros procesos lean el archivo. En resumen, un compartido el bloqueo puede ser retenido por múltiples procesos. Después de obtener un bloqueo compartido, el consumidor El programa lee los bytes de uno en uno del archivo, imprime los bytes en la salida estándar, libera el bloqueo, cierra el archivo y finaliza.

Aquí está el resultado de los dos programas lanzados desde el mismo terminal con % como la línea de comandos:

% ./producer
Process 29255 has written to data file...

% ./consumer
Now is the winter of our discontent
Made glorious summer by this sun of York

En este primer ejemplo de código, los datos compartidos a través de IPC son texto:dos líneas de la obra de Shakespeare Richard III . Sin embargo, el contenido del archivo compartido puede ser bytes voluminosos y arbitrarios (por ejemplo, una película digitalizada), lo que hace que compartir archivos sea un mecanismo de IPC impresionantemente flexible. La desventaja es que el acceso a los archivos es relativamente lento, ya sea que el acceso implique lectura o escritura. Como siempre, la programación viene con compensaciones. El siguiente ejemplo tiene la ventaja de IPC a través de la memoria compartida, en lugar de los archivos compartidos, con el correspondiente impulso en el rendimiento.

Memoria compartida

Los sistemas Linux proporcionan dos API separadas para la memoria compartida:la API System V heredada y la POSIX más reciente. Sin embargo, estas API nunca deben mezclarse en una sola aplicación. Una desventaja del enfoque POSIX es que las funciones aún están en desarrollo y dependen de la versión del kernel instalada, lo que afecta la portabilidad del código. Por ejemplo, la API POSIX, de forma predeterminada, implementa la memoria compartida como un archivo asignado a la memoria :para un segmento de memoria compartida, el sistema mantiene un archivo de respaldo con los contenidos correspondientes. La memoria compartida en POSIX se puede configurar sin un archivo de respaldo, pero esto puede afectar la portabilidad. Mi ejemplo usa la API POSIX con un archivo de respaldo, que combina los beneficios del acceso a la memoria (velocidad) y el almacenamiento de archivos (persistencia).

El ejemplo de memoria compartida tiene dos programas, llamados memwriter y lector de memoria y usa un semáforo para coordinar su acceso a la memoria compartida. Cada vez que la memoria compartida entra en escena con un escritor , ya sea en multiprocesamiento o subprocesos múltiples, también lo hace el riesgo de una condición de carrera basada en la memoria; por lo tanto, el semáforo se usa para coordinar (sincronizar) el acceso a la memoria compartida.

El memwriter El programa debe iniciarse primero en su propia terminal. El lector de memoria El programa se puede iniciar (en una docena de segundos) en su propia terminal. La salida del memreader es:

This is the way the world ends...

Más recursos de Linux

  • Hoja de trucos de los comandos de Linux
  • Hoja de trucos de comandos avanzados de Linux
  • Curso en línea gratuito:Descripción general técnica de RHEL
  • Hoja de trucos de red de Linux
  • Hoja de trucos de SELinux
  • Hoja de trucos de los comandos comunes de Linux
  • ¿Qué son los contenedores de Linux?
  • Nuestros últimos artículos sobre Linux

Cada archivo fuente tiene documentación en la parte superior que explica las banderas de enlace que se incluirán durante la compilación.

Comencemos con una revisión de cómo funcionan los semáforos como mecanismo de sincronización. Un semáforo general también se denomina semáforo de conteo , ya que tiene un valor (normalmente inicializado en cero) que se puede incrementar. Considere una tienda que alquila bicicletas, con cien de ellas en stock, con un programa que los empleados usan para hacer los alquileres. Cada vez que se alquila una bicicleta, el semáforo se incrementa en uno; cuando se devuelve una bicicleta, el semáforo se decrementa en uno. Los alquileres pueden continuar hasta que el valor llegue a 100, pero luego deben detenerse hasta que se devuelva al menos una bicicleta, lo que reduce el semáforo a 99.

Un semáforo binario es un caso especial que requiere solo dos valores:0 y 1. En esta situación, un semáforo actúa como un mutex :una construcción de exclusión mutua. El ejemplo de memoria compartida usa un semáforo como mutex. Cuando el valor del semáforo es 0, el memwriter solo puede acceder a la memoria compartida. Después de escribir, este proceso incrementa el valor del semáforo, lo que permite que el memreader para leer la memoria compartida.

Ejemplo 3. Código fuente para el memwriter proceso

/** Compilation: gcc -o memwriter memwriter.c -lrt -lpthread **/
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include "shmem.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1);
}

int main() {
  int fd = shm_open(BackingFile,      /* name from smem.h */
                    O_RDWR | O_CREAT, /* read/write, create if needed */
                    AccessPerms);     /* access permissions (0644) */
  if (fd < 0) report_and_exit("Can't open shared mem segment...");

  ftruncate(fd, ByteSize); /* get the bytes */

  caddr_t memptr = mmap(NULL,       /* let system pick where to put segment */
                        ByteSize,   /* how many bytes */
                        PROT_READ | PROT_WRITE, /* access protections */
                        MAP_SHARED, /* mapping visible to other processes */
                        fd,         /* file descriptor */
                        0);         /* offset: start at 1st byte */
  if ((caddr_t) -1  == memptr) report_and_exit("Can't get segment...");

  fprintf(stderr, "shared mem address: %p [0..%d]\n", memptr, ByteSize - 1);
  fprintf(stderr, "backing file:       /dev/shm%s\n", BackingFile );

  /* semaphore code to lock the shared mem */
  sem_t* semptr = sem_open(SemaphoreName, /* name */
                           O_CREAT,       /* create the semaphore */
                           AccessPerms,   /* protection perms */
                           0);            /* initial value */
  if (semptr == (void*) -1) report_and_exit("sem_open");

  strcpy(memptr, MemContents); /* copy some ASCII bytes to the segment */

  /* increment the semaphore so that memreader can read */
  if (sem_post(semptr) < 0) report_and_exit("sem_post");

  sleep(12); /* give reader a chance */

  /* clean up */
  munmap(memptr, ByteSize); /* unmap the storage */
  close(fd);
  sem_close(semptr);
  shm_unlink(BackingFile); /* unlink from the backing file */
  return 0;
}

Esta es una descripción general de cómo el memwriter y lector de memoria los programas se comunican a través de la memoria compartida:

  • El memwriter El programa, que se muestra arriba, llama al shm_open función para obtener un descriptor de archivo para el archivo de respaldo que el sistema coordina con la memoria compartida. En este punto, no se ha asignado memoria. La llamada subsiguiente a la función engañosamente nombrada ftruncate :
    ftruncate(fd, ByteSize); /* get the bytes */

    asigna ByteSize bytes, en este caso, unos modestos 512 bytes. El memwriter y lector de memoria los programas acceden solo a la memoria compartida, no al archivo de respaldo. El sistema es responsable de sincronizar la memoria compartida y el archivo de respaldo.

  • El memwriter luego llama al mmap function:
    caddr_t memptr = mmap(NULL,       /* let system pick where to put segment */
                          ByteSize,   /* how many bytes */
                          PROT_READ | PROT_WRITE, /* access protections */
                          MAP_SHARED, /* mapping visible to other processes */
                          fd,         /* file descriptor */
                          0);         /* offset: start at 1st byte */

    para obtener un puntero a la memoria compartida. (El lector de memoria hace una llamada similar). El tipo de puntero caddr_t comienza con una c para calloc , una función del sistema que inicializa el almacenamiento asignado dinámicamente a ceros. El memwriter utiliza el memptr para el posterior escribir operación, utilizando la biblioteca strcpy (copia de cadena) función.

  • En este punto, el memwriter está listo para escribir, pero primero crea un semáforo para garantizar el acceso exclusivo a la memoria compartida. Se produciría una condición de carrera si el memwriter estaban escribiendo mientras el memreader estaba leyendo. Si la llamada a sem_open tiene éxito:
    sem_t* semptr = sem_open(SemaphoreName, /* name */
                             O_CREAT,       /* create the semaphore */
                             AccessPerms,   /* protection perms */
                             0);            /* initial value */

    entonces la escritura puede continuar. El nombre del semáforo (cualquier nombre único que no esté vacío servirá) identifica el semáforo tanto en memwriter y el lector de memoria . El valor inicial de cero da el creador del semáforo, en este caso, el memwriter , el derecho a proceder, en este caso, a la escribir operación.

  • Después de escribir, el memwriter incrementa el valor del semáforo a 1:
    if (sem_post(semptr) < 0) ..

    con una llamada al sem_post función. Incrementar el semáforo libera el bloqueo mutex y habilita el memreader para realizar su lectura operación. En buena medida, el memwriter también desasigna la memoria compartida del memwriter espacio de direcciones:

    munmap(memptr, ByteSize); /* unmap the storage *

    Esto bloquea el memwriter de un mayor acceso a la memoria compartida.

Ejemplo 4. Código fuente para el memreader proceso

/** Compilation: gcc -o memreader memreader.c -lrt -lpthread **/
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include "shmem.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1);
}

int main() {
  int fd = shm_open(BackingFile, O_RDWR, AccessPerms);  /* empty to begin */
  if (fd < 0) report_and_exit("Can't get file descriptor...");

  /* get a pointer to memory */
  caddr_t memptr = mmap(NULL,       /* let system pick where to put segment */
                        ByteSize,   /* how many bytes */
                        PROT_READ | PROT_WRITE, /* access protections */
                        MAP_SHARED, /* mapping visible to other processes */
                        fd,         /* file descriptor */
                        0);         /* offset: start at 1st byte */
  if ((caddr_t) -1 == memptr) report_and_exit("Can't access segment...");

  /* create a semaphore for mutual exclusion */
  sem_t* semptr = sem_open(SemaphoreName, /* name */
                           O_CREAT,       /* create the semaphore */
                           AccessPerms,   /* protection perms */
                           0);            /* initial value */
  if (semptr == (void*) -1) report_and_exit("sem_open");

  /* use semaphore as a mutex (lock) by waiting for writer to increment it */
  if (!sem_wait(semptr)) { /* wait until semaphore != 0 */
    int i;
    for (i = 0; i < strlen(MemContents); i++)
      write(STDOUT_FILENO, memptr + i, 1); /* one byte at a time */
    sem_post(semptr);
  }

  /* cleanup */
  munmap(memptr, ByteSize);
  close(fd);
  sem_close(semptr);
  unlink(BackingFile);
  return 0;
}

Tanto en el memwriter y lector de memoria programas, las funciones de memoria compartida de mayor interés son shm_open y mapa :en caso de éxito, la primera llamada devuelve un descriptor de archivo para el archivo de respaldo, que luego usa la segunda llamada para obtener un puntero al segmento de memoria compartida. Las llamadas a shm_open son similares en los dos programas excepto que el memwriter programa crea la memoria compartida, mientras que el memreader solo accede a esta memoria ya creada:

int fd = shm_open(BackingFile, O_RDWR | O_CREAT, AccessPerms); /* memwriter */
int fd = shm_open(BackingFile, O_RDWR, AccessPerms);           /* memreader */

Con un descriptor de archivo en la mano, las llamadas a mmap son iguales:

caddr_t memptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

El primer argumento de mmap es NULO , lo que significa que el sistema determina dónde asignar la memoria en el espacio de direcciones virtuales. Es posible (pero complicado) especificar una dirección en su lugar. El MAP_SHARED indica que la memoria asignada se puede compartir entre procesos, y el último argumento (en este caso, cero) significa que el desplazamiento de la memoria compartida debe ser el primer byte. El tamaño El argumento especifica el número de bytes que se asignarán (en este caso, 512) y el argumento de protección indica que la memoria compartida se puede escribir y leer.

Cuando el memwriter el programa se ejecuta con éxito, el sistema crea y mantiene el archivo de respaldo; en mi sistema, el archivo es /dev/shm/shMemEx , con shMemEx como mi nombre (dado en el archivo de encabezado shmem.h ) para el almacenamiento compartido. En la versión actual del memwriter y lector de memoria programas, la declaración:

shm_unlink(BackingFile); /* removes backing file */

elimina el archivo de respaldo. Si desvincular se omite la declaración, luego el archivo de respaldo persiste después de que finaliza el programa.

El lector de memoria , como el memwriter , accede al semáforo a través de su nombre en una llamada a sem_open . Pero el memreader luego entra en un estado de espera hasta que el memwriter incrementa el semáforo, cuyo valor inicial es 0:

if (!sem_wait(semptr)) { /* wait until semaphore != 0 */

Una vez finalizada la espera, el memreader lee los bytes ASCII de la memoria compartida, limpia y finaliza.

La API de memoria compartida incluye operaciones explícitamente para sincronizar el segmento de memoria compartida y el archivo de respaldo. Estas operaciones se han omitido del ejemplo para reducir el desorden y mantener el enfoque en el código de semáforo y el intercambio de memoria.

El memwriter y lector de memoria es probable que los programas se ejecuten sin inducir una condición de carrera incluso si se elimina el código de semáforo:el memwriter crea el segmento de memoria compartida y escribe inmediatamente en él; el lector de memoria ni siquiera puede acceder a la memoria compartida hasta que se haya creado. Sin embargo, las mejores prácticas requieren que el acceso a la memoria compartida se sincronice siempre que se produzca una escritura. la operación está en la combinación, y la API del semáforo es lo suficientemente importante como para resaltarla en un ejemplo de código.

Conclusión

Los ejemplos de archivos compartidos y memoria compartida muestran cómo los procesos pueden comunicarse a través del almacenamiento compartido , archivos en un caso y segmentos de memoria en el otro. Las API para ambos enfoques son relativamente sencillas. ¿Estos enfoques tienen un inconveniente común? Las aplicaciones modernas a menudo se ocupan de la transmisión de datos, de hecho, con flujos de datos masivamente grandes. Ni los enfoques de archivos compartidos ni los de memoria compartida son adecuados para flujos de datos masivos. Los canales de un tipo u otro se adaptan mejor. Por lo tanto, la Parte 2 presenta canales y colas de mensajes, nuevamente con ejemplos de código en C.

[Descargar la guía completa de comunicación entre procesos en Linux]


Linux
  1. Presentamos la guía para la comunicación entre procesos en Linux

  2. Comunicación entre procesos en Linux:sockets y señales

  3. Conceptos básicos de los permisos de archivos de Linux

  4. Recomendación de comunicación entre procesos

  5. Ubuntu Linux - VHDX compartido

Menos comando en Linux

Comando Gzip en Linux

Comando Gunzip en Linux

Comando Stat en Linux

¿Qué es umask en Linux?

Cómo vincular un archivo en Linux