16.2.05

Sincroniazación entre procesos

El siguiente porgrama crea 2 hijos. Uno de ellos pondrá los números del 1 al 5 en una variable compartida y el otro leerá esos valores y los mostrará por pantalla.

El primer procso pone el número 1 en la variable compartida, después, el segundo proceso lee el valor 1 y lo muestra por pantalla. Después el primer proceso pone el número 2 en la variable compartida, y así hasta el 5.

Para sincronizar ambos procesos se han utilizado dos grupos de un semáforo cada uno.



#include < errno.h>
#include < stdio.h>
#include < unistd.h>
#include < wait.h>
#include < sys/ipc.h>
#include < sys/sem.h>
#include < sys/stat.h>
#include < sys/types.h>

#include "sv.h"


union senum {
int val;
};

int main() {

pid_t pid;
int c, r;
int num;
int sem_prod, sem_cons;
struct sembuf arriba = {0,1,0};
struct sembuf abajo = {0,-1,0};
union senum arg;


sv_init();

sem_prod = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
sem_cons = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if ((sem_prod == -1) || (sem_cons==-1)) {
perror("Error creando semáforos");
return -1;
}

arg.val = 1;
r = semctl(sem_prod, 0, SETVAL, arg);
if (r == -1) {
perror("Error incializando el semáforo del productor - ");
//Borrar los semáforos
return -1;
}

for (c = 0; c < 2; c++) {
pid = fork();
if (pid == -1) {
perror("Error creando procesos - ");
// Borrar semáforos.
return -1;
}

if (pid == 0) {

// Productor
if (c==0) {
for (num = 1; num < 6; num++) {
// Mi turno de producir
r = semop(sem_prod, &abajo, 1);
if (r == -1) {
perror("Error bajando el semáforo del productor - ");
// Eliminar semáforos
return -1;
}

printf("Valor producido: %i\n", num);
sv_set(num);

// El turno de consumir
r = semop(sem_cons, &arriba, 1);
if (r == -1) {
perror("Error subiendo el semáforo del consumidor - ");
// Eso
return -1;
}
}
exit(0);
}

// Consumidor
if (c==1) {
for (num = 0; num < 5; num++) {
// Voy a consumir 5 números
r = semop(sem_cons, &abajo, 1);
if (r == -1) {
perror("Error bajando semáforo del consumidor - ");
return -1;
}

printf("Valor consumido: %i\n", sv_get() );

// Hora de producir
r = semop(sem_prod, &arriba, 1);
if (r == -1) {
perror("Error subiendo semáforo del productos - ");
return -1;
}
}
exit(0);
}

}
}

pid = wait(&r);

while ( (pid != -1) ||
( (pid == -1) && (errno == EINTR)) )
pid = wait(&r);

r = semctl(sem_prod, 0, IPC_RMID);
if (r==-1) {
perror("Error eliminando semáforo productor.");
}
r = semctl(sem_cons, 0, IPC_RMID);
if (r == -1) {
perror("Error eliminando semáforo consumidor. ");
}

sv_finish();

printf("Fin.\n");
return 0;
}


Acceso a un recurso compartido

Saludos.

El siguinte código crea cuatro procesos hijos. Cada hijo incrementará una variable compartida en 5 unidades. Para garantizar la exclusión mútua sobre la variable compartida se utiliza un grupo de un semáforo que funciona como semáforo binario.

Hay que cabiar todas las " de los includes por mayor y menor excepto el include de sv.h.


/**
Implementa un semáforo binario
utilizando directamente las funciones del sistema
**/

#include "sv.h"

#include "stdio.h"
#include "wait.h>
#include "unistd.h"
#include "sys/types.h"

#include "sys/stat.h"
#include "sys/ipc.h"
#include "sys/sem.h"


// Incrementa en 5 la variable compartida
#define INC 5

void inc(int);


// Esta unión hay que declararla
union semun {
int val;
struct semid_ds *buf;
ushort *array;
};

int main() {
int c;
int hijos = 4;
pid_t pid;

int semaforo;
union semun arg;
int r;
// { semaforo, valor, banderas }
struct sembuf arriba = {0, 1, 0};
struct sembuf abajo = {0, -1, 0};


sv_init();
printf("Valor de inicio: %i\n", sv_val());

// Crear e inicializar un semaforo binario
// para garantizar la exclusion mutua.
semaforo = semget(42, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if (semaforo == -1) {
perror ("Error en la creación del semáforo - ");
return -1;
}

arg.val = 1;
r = semctl(semaforo, 0, SETVAL, arg);
if (r == -1) {
perror ("Error inicializando semáforo - ");
// Hay que eliminarlo.
return -1;
}


for (c = 0; c < hijos; c++) {
pid = fork();
if (pid == -1) {
perror("Error en fork - ");
exit(-1);
}
if (pid == 0) {

// Decrementar semaforo
r = semop(semaforo, &abajo, 1);
if (r == -1) {
perror("Error decrementando semáforo - ");
// Eliminar el semáforo
return -1;
}

inc(c);

// Incrementar semaforo
r = semop(semaforo, &arriba, 1);
if (r == -1) {
perror("Error incrementando semáforo - ");
// Es necesario eliminarlo
return -1;
}

exit(0);
}
}

while ( wait(NULL)!=-1 );

printf("-------------------------------\n");
printf("Valor esperado: %i\n", hijos * INC);
printf("Valor real: %i", sv_val());

// Borrar semaforo
r = semctl(semaforo, 0, IPC_RMID);
if (r == -1)
{
perror("Error elminando semáforo - ");
return -1;
}

sv_finish();
printf("\n");
}


//----------------------------
void inc(int id) {
int c;
int tmp;
for(c=0; c < INC; c++) {
tmp = sv_get();
if ( (id == 0) && (c < 2)) sleep(1);
tmp++;
sv_set(tmp);
}
}

14.2.05

Libería para implementar una variable compartida

A continuación se incluye el código de una librería que permite crear y manipular una variable compartida, esto es, una variable común para todos los procesos que utilicen esta librería. Así, los cambios que haga un proceso sobre esta variable, serán visibles para los demás procesos.

Esta librería es un buen ejemplo de como utilizar el mecanismo IPC de memoria compartida.

Esta librería se utilizará en ejemplos posteriores con semáforos.

sv.h


#ifndef _SV_
#define _SV_

/**
Gestiona una variable entera mediante memoria compartida.
La variable es común a todos los procesos creados
después de llamar a sv_init();
**/


/**
Reserva la memoria compartida e inicializa la variable
a 0.
-1 si error.
**/
int sv_init();

/**
Libera la memoria compartida.
-1 si error.
**/
int sv_finish();

/**
Devuelve el valor de la variable común
sv_get es un alias para sv_vsl
**/
int sv_val();
int sv_get();

/**
Incrementa la variable entera con el valor
indicado como parámetro y devuelve su nuevo valor.
**/
int sv_inc(int);

/**
Asigna un valor a la variable común
Devuelve el valor asignado.
**/
int sv_set(int);

#endif





sv.c


#include "stdio.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include "sys/stat.h"
#include "sys/types.h"

int s_id;
int *sv = NULL;

//---------------------------------
int sv_init() {
int tam = sizeof(int);
s_id = shmget(IPC_PRIVATE, tam, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if (s_id == -1)
return -1;
sv = (int *)shmat(s_id, NULL, 0);
if (sv == (int *)-1) {
sv = NULL;
return -1;
}
(*sv)=0;
return 1;
}

//--------------------------------
int sv_inc(int val) {
int tmp;
if (sv == NULL)
return -1;
tmp = (*sv);
tmp += val;
(*sv)= tmp;
return (tmp);
}

//--------------------------------
int sv_val() {
if (sv == NULL)
return 0;
return (*sv);
}

//--------------------------------
int sv_get() {
return sv_val();
}

//--------------------------------
int sv_set(int val) {
(*sv) = val;
return val;
}

//--------------------------------
int sv_finish() {
if (s_id == -1)
return -1;
return shmctl(s_id, IPC_RMID, 0);
}

Mensaje con preaviso

Saludos.

El siguiente código crea un proceso hijo que enviará cuatro mensajes al proceso padre, el cual los mostrará por pantalla. Para evitar que cualquier de los dos procesos se quede esperando en la tubería, el padre, antes de leer un mensaje de la tubería, avisará al hijo con una señal SIGUSR1. El hijo, al recibir la señal, pondrá un mensaje en la tubería y esperará a la siguiente señal.


#include "stdio.h"
#include "unistd.h"
#include "signal.h"
#include "wait.h"
#include "sys/types.h"

#define SIZE 256

int tub[2];
char msg[SIZE];

void manejador_hijo(int);

int main() {

int c = 0;
int status;
pid_t pid;

pipe(tub);
pid = fork();
if (pid == -1) {
perror("Error bifurcando proceso - ");
return -1;
}

if (pid == 0) {
if (signal (SIGUSR1, manejador_hijo) == SIG_ERR) {
perror("Error instalando manejador - ");
exit(-1);
}
close(tub[0]);
while (1)
pause();

printf("Hijo, terminación inesperada.\n");
exit(-1);
}

close(tub[1]);
while(c < 4) {
sleep(1);
if ( kill(pid, SIGUSR1) == -1) {
perror("Error enviando señal al hijo.");
return -1;
}

if (read(tub[0], msg, SIZE) == -1) {
perror("Error leyendo de la tuberia - ");
return -1;
}

printf("%i: %s\n", c, msg);
c++;
}

close(tub[0]);

if (kill(pid, SIGTERM) == -1) {
perror("Error terminando hijo.");
return -1;
}

while( pid != wait(&status) );

printf("Fin\n");
return 0;
}

//--------------------------------------------------

void manejador_hijo(int sig)
{
if ( signal (SIGUSR1, manejador_hijo) == SIG_ERR) {
perror("Error reinstalando el controlador.");
exit(-1);
}
strcpy(msg, "Mensaje del hijo.");
if ( write(tub[1], msg, strlen(msg)) == -1) {
perror("Error escribiendo en la tuberia.");
exit(-1);
}
}