Programación del sistema Linux C (09) gestión de procesos comunicación entre procesos

La importancia de la comunicación entre procesos es cómo permitir que múltiples procesos accedan a datos entre ellos. Hay muchas maneras de lograr esto en Linux.


1 Descripción general de la comunicación entre procesos

La comunicación entre procesos es permitir que múltiples procesos accedan entre sí, incluidos los datos de tiempo de ejecución y los segmentos de código de cada uno, lo cual es muy común en aplicaciones prácticas. El mecanismo de IPC simplemente proporciona un canal de transmisión de datos para la comunicación de datos.    

Mientras se ejecuta el proceso, su espacio de direcciones no es visible en relación con otros procesos (esto es solo el concepto de procesos tradicionales), son independientes en el sistema y no pueden acceder entre sí.

Los métodos comunes de comunicación entre procesos son: tuberías, tuberías FIFO, señales, semáforos, colas de mensajes, memoria compartida, sockets.

Para las tuberías, hay tuberías half-duplex y tuberías full-duplex:

  1. Canalización semidúplex: tubería anónima semidúplex FIFO
  2. Tubería full-duplex: tubería anónima full-duplex, llamada tubería full-duplex

2 tubos

2.1 El concepto de tuberías

Uno de los métodos de comunicación más comunes es realizar una tubería de circulación de datos entre dos procesos. La tubería puede ser unidireccional o bidireccional; la tubería es fácil de usar, pero tiene muchas limitaciones. Una tubería dúplex anónima no tiene un nombre real en el sistema y no se puede ver de ninguna manera en el sistema de archivos. Es solo un recurso del proceso y el sistema la borrará cuando finalice el proceso. La comunicación de la tubería debe buscarse utilizando el comando grep. Como sigue

$ls |grep ipc

Las tuberías se dividen en tuberías full-duplex y half-duplex en la dirección del flujo de datos. En el proceso de implementación específico, la tubería full-duplex es solo un poco diferente en la forma en que se abre el archivo (las reglas de operación también son algo diferentes, la tubería full-duplex es mucho más complicada que la mitad-duplex).

2.2 Tubería semidúplex anónima

Una tubería anónima no tiene nombre, y no hay un nombre de ruta para el descriptor de archivo utilizado en la tubería. Es decir, no hay archivos de importancia, solo dos descriptores de archivos asociados con un punto de índice en la memoria. Sus características son las siguientes:

  1. Los datos solo pueden moverse en una dirección.
  2. Solo se puede comunicar entre procesos que tienen un ancestro común, es decir, entre procesos primarios y secundarios / procesos hermanos.
  3. Sin embargo, las tuberías semidúplex son el método de comunicación más común.

En el entorno Linux, use la función de canalización para crear una canalización semidúplex anónima. El prototipo de la función es el siguiente:

#include <unistd.h>
int pipe(int pipefd[2]);

Consulte el manual de referencia de la función de Linux para más detalles . Para la matriz fd, no está asociada con ningún archivo conocido, que es el origen del nombre de la tubería anónima.

2.3 Operaciones de lectura y escritura de tuberías anónimas half-duplex

  • Al leer y escribir en la tubería, use las funciones de lectura y escritura para operar en la tubería. Cuando se ha cerrado un extremo de lectura, se generará una señal SIGPIPE, que indica que el extremo de lectura de la tubería se ha cerrado; y la operación de escritura devuelve -1, El valor de errno es EPIPE. Se puede capturar la señal SIGPIPE. Si el proceso de escritura no puede capturar / ignorar la señal SIGPIPE, el proceso de escritura se interrumpirá.
  • La tubería puede ser heredada. En circunstancias normales, la función de tubería y la función de horquilla se usan juntas, pero debe tenerse en cuenta: para mantener el orden de la tubería, cuando el proceso padre crea la tubería. Solo cuando el proceso hijo ha heredado la tubería, el proceso padre puede realizar la operación de cerrar la tubería.Si la tubería se cierra antes de la bifurcación, el proceso hijo no heredará la tubería disponible.
  • Cuando se lee una tubería, la función de lectura devuelve 0. Hay dos significados: uno es que no hay datos en la tubería y el final de escritura está cerrado, y el otro es que no hay datos en la tubería y el final de escritura aún está vivo. La situación debe tratarse por separado.

2.4 Funciones de biblioteca estándar para crear tuberías

En circunstancias normales, se utiliza un conjunto estándar de procedimientos para las operaciones de canalización, por lo que las operaciones anteriores se definen en dos funciones de biblioteca estándar popen y pclose en ANSI / ISO C. El prototipo de función en Linux es el siguiente:

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

Consulte el manual de referencia de la función de Linux para más detalles .


3 tubería FIFO

3.1 El concepto de tubería FIFO

FIFO también se conoce como una tubería famosa, es un tipo de archivo, que se puede ver en el sistema. El método de comunicación de FIFO es similar al de usar un archivo para transferir datos en el proceso, excepto que el archivo de tipo FIFO también tiene las características de una tubería y borra los datos al mismo tiempo cuando se leen los datos. El comando mkfifo en el shell puede crear una tubería famosa.

3.2 Creación de tubería FIFO

Crear un archivo FIFO es similar a crear un archivo. También se puede acceder a FIFO por nombre de ruta. Use la función mkfifo para realizar FIFO en Linux, el prototipo de la función es el siguiente:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

Consulte el manual de referencia de la función de Linux para más detalles .

3.3 Operaciones de lectura y escritura FIFO

Las funciones generales de E / S se pueden usar para archivos FIFO. Pero tenga en cuenta: cuando se usa la función de abrir para abrir un archivo FIFO, el indicador O_NONBLOCK del indicador de bandera del parámetro de función abierta está relacionado con el estado de retorno de la función. La relación lógica de configuración o no es la siguiente:

  1. Si se establece: la apertura de solo lectura vuelve inmediatamente. Al escribir solo abierto, si ningún proceso abre el FIFO para leer, devuelve -1. En otras palabras, en el caso de no bloqueo, la lectura y la escritura deben ser simultáneas.
  2. Si no está configurado: abra los bloques según la situación. La apertura de solo lectura se bloquea hasta que un proceso abre la FIFO para escritura, y solo la apertura de escritura se bloquea hasta que un proceso abre la FIFO para lectura.

Cuando todos los procesos de FIFO se han cerrado, se genera un carácter de fin de archivo para el proceso de lectura de FIFO. La aparición de FIFO ha resuelto el problema de una gran cantidad de archivos intermedios generados por el sistema en el proceso de solicitud. El shell puede llamar al FIFO para transferir datos de un proceso a otro. El sistema no tiene que preocuparse por limpiar la basura innecesaria para el canal intermedio, o liberar los recursos del canal, puede ser utilizado por procesos posteriores y evitar Además, la limitación del alcance de las tuberías anónimas se puede aplicar entre procesos no relacionados.

3.4 Desventajas de FIFO

Para la arquitectura servidor-cliente, se puede procesar, pero la premisa es que la interfaz FIFO proporcionada por el servidor debe conocerse de antemano, de la siguiente manera:

  1. El cliente envía una solicitud al servidor: necesita conocer la FIFO pública del servidor.
  2. El servidor devuelve información al cliente: se necesita crear un FIFO dedicado para que cada cliente responda. Cuando el número alcanza un cierto nivel, el FIFO sobrecargará el servidor y hará que el servidor se bloquee.

4 Introducción del mecanismo de comunicación System V IPC / POSIX IPC

System V IPC incluye tres mecanismos de comunicación: cola de mensajes, semáforo y memoria compartida. Esta es una forma antigua y ha sido reemplazada por POSIX IPC en versiones recientes. POSIX IPC y System V IPC se refieren a lo mismo, pero la función utilizada es diferente, la implementación también es diferente. El mecanismo de IPC es diferente de las tuberías y FIFO. Pipes y FIFO se basan en el sistema de archivos, mientras que IPC se basa en el núcleo, puede usar ipcs para ver el estado actual de los objetos IPC del sistema.

4.1 El concepto de objetos IPC

El objeto IPC es una herramienta para la comunicación entre procesos que está activa a nivel del núcleo. Se hace referencia al objeto IPC existente y se accede a él a través de su identificador. Este identificador es un número entero no negativo que identifica de forma exclusiva un objeto IPC. Este objeto IPC puede ser cualquier cola de mensaje, semáforo y memoria compartida. Tipo.

En Linux, los identificadores se declaran como enteros, por lo que el valor máximo de los posibles identificadores es 65535 (2 ^ 16). Nota: El identificador aquí es diferente del descriptor de archivo. Cuando el archivo se abre con la función abrir, el valor del descriptor de archivo devuelto es el subíndice de la matriz de descriptores de archivo más pequeña disponible en el proceso actual. Cuando se elimina / crea el objeto IPC, el valor del identificador correspondiente continuará aumentando. Después de alcanzar el valor máximo, el retorno a cero se asignará y utilizará cíclicamente.

El identificador IPC solo resuelve el problema de acceder internamente a un objeto IPC. Cómo permitir que múltiples procesos accedan a un objeto IPC específico también requiere una clave externa. Cada objeto IPC está asociado con una clave. Esto resuelve el problema de la convergencia de múltiples procesos en un objeto IPC.

Hay varias formas de permitir que múltiples procesos sepan que existe tal clave:

  1. Use el archivo como canal intermedio, cree un proceso de objeto IPC y use la clave IPC_PRIVATE para establecer con éxito el objeto IPC y almacene el identificador devuelto en un archivo. Otros procesos se refieren a la comunicación de objetos IPC leyendo este identificador.
  2. Defina una clave que sea reconocida por múltiples procesos. Cada proceso usa esta clave para referirse al objeto IPC. Para un proceso que crea un objeto IPC, si el valor de la clave se ha combinado con un objeto IPC, debe eliminar el objeto IPC y crear uno nuevo. IPC objetos.
  3. En la comunicación multiproceso, para que una clave especificada haga referencia a un objeto IPC, puede no ser extensible y, en el caso de que el valor de la clave haya sido combinado por un objeto IPC. Por lo tanto, debe eliminar este objeto existente y luego crear uno nuevo. Pero esto puede afectar a otros procesos que utilizan este objeto. La función ftok puede resolver este problema hasta cierto punto.

La función ftok puede generar un valor clave utilizando dos parámetros. El prototipo de la función es el siguiente:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

Consulte el manual de referencia de la función de Linux para más detalles . La función combina los valores parciales del miembro st_dev y el miembro st_ino de la estructura estadística en el archivo de ruta de parámetros con el octavo bit del parámetro proj_id para generar un valor clave. Debido a que solo se utilizan los valores parciales del miembro st_dev y el miembro st_ino, se perderá información y no se excluye que dos archivos diferentes usen la misma ID para obtener el mismo valor clave.

El sistema guarda una estructura ipc_perm para cada objeto IPC, que describe la autoridad y el propietario del objeto IPC. Cada versión del contenido del núcleo tiene una estructura ipc_perm diferente, que describe la autoridad y el propietario del objeto IPC Cada versión es diferente. Para cada objeto IPC, el sistema comparte una estructura de datos struct ipc_perm para almacenar información de permisos para determinar si una operación IPC puede acceder al objeto IPC. La estructura ipc_perm se implementa de la siguiente manera:

struct ipc_perm
{
    __kernel_key_t key;
    __kernel_uid_t uid;
    __kernel_gid_t gid;
    __kernel_uid_t cuid;
    __kernel_gid_t cgid;
    __kernel_mode_t mode;
    unsigned short seq;
}; 

Solo la raíz / proceso que creó el objeto IPC tiene derecho a cambiar el valor de la estructura ipc_perm. Los defectos de los objetos IPC son los siguientes:

  1. Interfaz de programación demasiado complicada, en comparación con otros métodos de comunicación, la cantidad de código requerida por IPC aumenta significativamente.
  2. IPC no utiliza un sistema de archivos común. Por lo tanto, no se pueden usar operaciones de E / S estándar; se agregan nuevas funciones; dado que no se usan descriptores de archivos, no se pueden usar múltiples funciones de selección / sondeo de funciones de E / S para operar objetos IPC.
  3. Falta de mecanismo de recuperación de recursos, generalmente solo el proceso lee el mensaje / IPC propietario / superusuario elimina este objeto. Este también es un mecanismo de recuperación de recursos que IPC carece en relación con las tuberías / FIFO.

4.2 Comandos del sistema IPC

Use el comando ipcs en el shell para mostrar el estado de IPC. Salida de información por ipcs:

  • clave identifica la clave externa del objeto IPC
  • shmid identifica el identificador del objeto IPC
  • propietario identifica al usuario al que pertenece el IPC
  • permisos permisos de logo

Eliminar un comando de objeto IPC:

$ipcrm -m shmid号  

 Ver el comando de estado del IPC del sistema actual:

$ipcs -m

5 memoria compartida

La memoria compartida es el método más rápido de toda la comunicación del espacio de procesos, es un recurso que existe a nivel del núcleo. Hay archivos correspondientes descritos en el directorio del sistema de archivos / proc.

5.1 El concepto de memoria compartida

El mecanismo de memoria compartida se basa en el principio: cuando el núcleo del sistema asigna una dirección a un proceso, la dirección física de un proceso puede ser discontinua a través del mecanismo de paginación, y al mismo tiempo, una sección de memoria puede asignarse a diferentes procesos al mismo tiempo. Para cada segmento de almacenamiento compartido, el núcleo mantiene una estructura de tipo shmid_ds para él. La definición de la estructura shmid_ds es la siguiente:

struct shmid_ds
{
    struct ipc_perm shm_perm;                /* operation perms */
    int shm_segsz;                               /* size of segment (bytes) */
    __kernel_time_t shm_atime;          /* last attach time */
    __kernel_time_t shm_dtime;           /* last detach time */
    __kernel_time_t shm_ctime;           /* last change time */
    __kernel_ipc_pid_t shm_cpid;           /* pid of creator */
    __kernel_ipc_pid_t shm_lpid;           /* pid of last operator */
    unsigned short shm_nattch;                /* no. of current attaches */
    unsigned short shm_unused;                /* compatibility */
    void *shm_unused2;                          /* ditto - used by DIPC */
    void *shm_unused3;                          /* unused */
}; 

La estructura shmid_ds será ligeramente diferente según las diferentes versiones del núcleo del sistema, y ​​el tamaño del segmento de almacenamiento compartido estará limitado en los diferentes sistemas. Consulte los manuales correspondientes al realizar la solicitud.

5.2 Creación de memoria compartida.

Use la función shmget para crear / abrir un área de memoria compartida en Linux. El prototipo de la función de función shmget es el siguiente:

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

Consulte el manual de referencia de la función de Linux para más detalles .

5.3 Operación de memoria compartida

Debido al tipo de recurso especial de memoria compartida, la operación es diferente de los archivos ordinarios, lo que requiere funciones de operación únicas. Linux usa memoria compartida para múltiples operaciones. El prototipo de la función de administración de memoria compartida es el siguiente:

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

Consulte el manual de referencia de la función de Linux para más detalles . Nota:

  1. Después de la bifurcación, el proceso secundario hereda la dirección de memoria compartida conectada. Después de la ejecución, el proceso secundario se desconecta automáticamente de la dirección de memoria compartida conectada. Una vez que finaliza el proceso, la dirección de memoria compartida conectada se separará automáticamente
  2. Después de ejecutar la función, se conecta la memoria compartida con el identificador de memoria compartida de shmid. Después de que la conexión se realiza correctamente, el objeto del área de memoria compartida se asigna al espacio de direcciones del proceso de llamada, y luego se puede acceder como el espacio local.

@ 3 Cuando finaliza la operación en el segmento de memoria compartida, se debe llamar a la función shmdt para desconectar la memoria compartida. El prototipo de la función es el siguiente:

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

Consulte el manual de referencia de la función de Linux para más detalles .

5.4 Notas sobre el uso de memoria compartida

  • En comparación con otros métodos, la memoria compartida hace que los datos sean más transparentes durante la lectura y la escritura. Cuando una pieza de memoria compartida se importa con éxito, es equivalente a un puntero de cadena para apuntar a una pieza de memoria, a la que puede acceder libremente el usuario actual. Sin embargo, la desventaja de esto es que se requiere un control estructural adicional durante el proceso de escritura / lectura de datos, y al mismo tiempo, también se requiere un código adicional para ayudar al mecanismo de memoria compartida en la sincronización multiproceso / exclusión mutua.
  • En el segmento de memoria compartida, el final predeterminado de la cadena es el final de un mensaje. Cada proceso sigue esta regla y no destruirá la integridad de los datos.

6 semáforo

6.1 El concepto de semáforo

El semáforo en sí no tiene la función de transmisión de datos. Es solo un identificador de recursos externos. Se puede utilizar para determinar si hay recursos externos disponibles. El semáforo es responsable de la exclusión y sincronización de datos en este proceso. Al solicitar un recurso representado por un semáforo, el proceso necesita primero leer el valor del semáforo para determinar si el recurso correspondiente está disponible;

  1. Cuando el valor del semáforo es mayor que 0, significa que hay recursos para solicitar;
  2. Cuando es igual a 0, significa que no hay recursos disponibles, por lo que el proceso se suspenderá hasta que haya recursos disponibles.

Cuando el proceso ya no utiliza un recurso compartido controlado por un semáforo, el valor de este semáforo es +1, y el aumento o disminución del semáforo es una operación atómica. Esto se debe a que la función principal del semáforo es mantener la exclusión mutua / multiproceso del recurso El acceso sincrónico, y en la creación / inicialización de semáforos, las operaciones atómicas no están garantizadas. El kernel configurará una estructura shmid_ds para cada conjunto de señales y utilizará una estructura sin nombre para identificar un semáforo. La definición varía según el entorno de Linux. La estructura shmid_ds se define de la siguiente manera:

struct shmid_ds {
    struct ipc_perm    shm_perm;                /* operation perms */
    int    shm_segsz;                              /* size of segment (bytes) */
    __kernel_time_t    shm_atime;         /* last attach time */
    __kernel_time_t    shm_dtime;         /* last detach time */
    __kernel_time_t    shm_ctime;         /* last change time */
    __kernel_ipc_pid_t shm_cpid;         /* pid of creator */
    __kernel_ipc_pid_t shm_lpid;          /* pid of last operator */
    unsigned short     shm_nattch;              /* no. of current attaches */
    unsigned short     shm_unused;              /* compatibility */
    void               *shm_unused2;                /* ditto - used by DIPC */
    void               *shm_unused3;                /* unused */
};

La estructura de datos shmid_ds representa cada memoria compartida recién creada. Cuando shmget () crea una nueva pieza de memoria compartida, devuelve un identificador que puede usarse para hacer referencia a la estructura de datos shmid_ds de la memoria compartida.

6.2 Creación de semáforos

Al igual que la memoria compartida, el sistema también necesita personalizar una serie de funciones de operación propietarias (semget, semctl, etc.) para los semáforos. En Linux, use la función semget para crear / obtener un ID de conjunto de semáforo. El prototipo es el siguiente:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

Consulte el manual de referencia de la función de Linux para más detalles .

6.3 Operación del semáforo

Entre los tres tipos de objetos IPC, la función de operación del conjunto de semáforos es mucho más complicada que las funciones de operación de los otros dos tipos, y el uso del mismo semáforo también es más extenso que los otros dos. Semaphore también tiene su propia operación exclusiva. Use la función semctl para operar bajo Linux, el prototipo de la función es el siguiente:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

Consulte el manual de referencia de la función de Linux para más detalles .


7 Cola de mensajes

7.1 Concepto de cola de mensajes

Una cola de mensajes es un grupo de datos organizados en una estructura de lista vinculada, y se almacena en el núcleo. Es un método de transmisión de datos al que hace referencia cada proceso a través de un identificador de cola de mensajes. También es mantenido por el núcleo y es el método de transmisión de datos más operable entre los tres objetos IPC. En la cola de mensajes, los mensajes se pueden recuperar de acuerdo a tipos de datos específicos a voluntad. Por supuesto, para mantener la lista vinculada, se requieren más recursos de memoria, y la lectura y escritura de datos son más complicadas que la memoria compartida, y la sobrecarga de tiempo también es mayor.

7.2 Crear una cola de mensajes

En Linux, use la función msgget para crear / abrir una cola. El prototipo de la función es el siguiente:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

Consulte el manual de referencia de la función de Linux para más detalles .  

7.3 Operaciones de la cola de mensajes, lectura y escritura

En Linux, use las funciones msgsnd y msgrcv para leer y escribir la cola de mensajes. El prototipo de la función es el siguiente:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //消息队列写操作
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); //消息队列读操作

Consulte el manual de referencia de la función de Linux para más detalles .

Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/vviccc/article/details/105159556
Recomendado
Clasificación