C Language
Comunicación entre procesos (IPC)
Buscar..
Introducción
Los mecanismos de comunicación entre procesos (IPC) permiten que diferentes procesos independientes se comuniquen entre sí. El estándar C no proporciona ningún mecanismo de IPC. Por lo tanto, todos estos mecanismos están definidos por el sistema operativo del host. POSIX define un extenso conjunto de mecanismos de IPC; Windows define otro conjunto; Y otros sistemas definen sus propias variantes.
Semáforos
Los semáforos se utilizan para sincronizar operaciones entre dos o más procesos. POSIX define dos conjuntos diferentes de funciones de semáforo:
- 'System V IPC' -
semctl()
,semop()
,semget()
. - 'POSIX Semaphores' -
sem_close()
,sem_destroy()
,sem_getvalue()
,sem_init()
,sem_open()
,sem_post()
,sem_trywait()
,sem_unlink()
.
Esta sección describe los semáforos de IPC del Sistema V, llamados así porque se originaron con el Sistema V de Unix.
Primero, deberás incluir los encabezados requeridos. Las versiones anteriores de POSIX requerían #include <sys/types.h>
; POSIX moderno y la mayoría de los sistemas no lo requieren.
#include <sys/sem.h>
Luego, deberá definir una clave tanto para el padre como para el niño.
#define KEY 0x1111
Esta clave debe ser la misma en ambos programas o no se referirán a la misma estructura de IPC. Hay formas de generar una clave acordada sin tener que codificar su valor.
A continuación, dependiendo de su compilador, puede o no necesitar hacer este paso: declarar una unión con el fin de realizar operaciones de semáforo.
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
A continuación, defina sus semwait
try ( semwait
) y raise ( semsignal
). Los nombres P y V proceden del holandés.
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
Ahora, comience por obtener el ID para su semáforo de IPC.
int id;
// 2nd argument is number of semaphores
// 3rd argument is the mode (IPC_CREAT creates the semaphore set if needed)
if ((id = semget(KEY, 1, 0666 | IPC_CREAT) < 0) {
/* error handling code */
}
En el padre, inicialice el semáforo para tener un contador de 1.
union semun u;
u.val = 1;
if (semctl(id, 0, SETVAL, u) < 0) { // SETVAL is a macro to specify that you're setting the value of the semaphore to that specified by the union u
/* error handling code */
}
Ahora, puedes disminuir o incrementar el semáforo según lo necesites. Al comienzo de su sección crítica, disminuye el contador utilizando la función semop()
:
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
Para incrementar el semáforo, usa &v
lugar de &p
:
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
Tenga en cuenta que cada función devuelve 0
en caso de éxito y -1
en caso de fallo. No verificar estos estados de retorno puede causar problemas devastadores.
Ejemplo 1.1: Carreras con hilos
El programa a continuación tendrá un proceso de fork
un niño y tanto el padre como el niño intentarán imprimir caracteres en el terminal sin ninguna sincronización.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
}
Salida (1ª carrera):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2da carrera):
aabbccAABddBCeeCffgDDghEEhFFGGHH
La compilación y ejecución de este programa debe darle una salida diferente cada vez.
Ejemplo 1.2: Evita competir con semáforos
Modificando el Ejemplo 1.1 para usar semáforos, tenemos:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 0x1111
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct sembuf p = { 0, -1, SEM_UNDO};
struct sembuf v = { 0, +1, SEM_UNDO};
int main()
{
int id = semget(KEY, 1, 0666 | IPC_CREAT);
if(id < 0)
{
perror("semget"); exit(11);
}
union semun u;
u.val = 1;
if(semctl(id, 0, SETVAL, u) < 0)
{
perror("semctl"); exit(12);
}
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(13);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(14);
}
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(15);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(16);
}
sleep(rand() % 2);
}
}
}
Salida:
aabbAABBCCccddeeDDffEEFFGGHHgghh
Compilar y ejecutar este programa te dará la misma salida cada vez.