
	TEMA VII
	Comunicacin entre procesos - Concurrencia


+ Concurrencia.

Cuando dos o ms procesos estn realizando una tarea en conjunto, normalmente
comparten uno o mas recursos (archivos por ejemplo).

Dada la naturaleza de un sistema operativo multitarea, pude darse el caso
en el cual un proceso este modificando o leyendo un dato del recursos, y
temporalmente es suspendido por el planificador del Sistema Operativo y
otro proceso, que realiza la misma tarea, es puesto en estado de ejecucin.
Si este proceso iba a modificar el recurso con otro valor diferente al
que tenia el otro proceso, se destruir el trabajo realizado y se provoca
una inconsistencia en el recurso. Esta condicin se conoce como un condicin
de competencia (race condition).

Este tipo de condiciones son difciles de detectar y de reproducir.

Por tanto, es necesario que siempre que un recurso se comparte por dos o
ms procesos, se maneje la concurrencia sobre dicho recurso. Para realizar
esto, se deben utilizar mecanismos de intercomunicacin entre procesos (IPCs)
tales como semforos, colas de mensajes y memoria compartida.

+ Un ejemplo de concurrencia.

El siguiente ejemplo lee un archivo contador, con contenido inicial de 0
(y el salto de linea, es decir 2 bytes).
Luego realiza una bifurcacin de procesos, y en conjunto, el padre
e hijos, modifican el archivo. 

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define CONTADOR "contador"


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


	int fd; /*descriptor del archivo a leer/escribir*/
	int pid; 
	int contador=0; /*variable contador*/
	int i;
	char sContador[4]; /*contenido del 
				archivo de maximo tres posiciones*/
	char mensaje[256]; /*buffer de mensaje para depurar*/
	int nProcs; /*numero de procesos a arrancar*/

	if (argc <=1 ) {
		printf("Ejemplo1 nProcesos\n");
		exit(1);
	}	

	nProcs=atoi(argv[1])-1;

	/*abrir el archivo para lectura y escritura*/

	fd= open(CONTADOR, O_RDWR);

	if (fd < 0) {
		perror("Error al abir el archivo");
		exit(1);
	}

	/*crear nProcs */
	for (i=0;i<nProcs;i++) {
		pid=fork();

		if (pid < 0) {
			perror("Error al crear al hijo");
			exit(2);
		} 
	}

	/*padre e hijo ejecutan el mismo codigo*/

	while ( contador < 99){
		int nbytes;	
	
		/*poner el descriptor al inicio del archivo*/	
		if ( lseek(fd,0,SEEK_SET) ){
			perror("Error en lseek");
		}
		

		/*leer el archivo*/
		nbytes= read(fd,sContador,sizeof(sContador));	

		if (nbytes < 0)  {
			perror("Error al leer el archivo");
			exit(2);
		}

		/*quitar \n y poner un nulo*/
		sContador[nbytes-1]=0;

		/*se escribe a salida estandar los mensajes*/
		sprintf(mensaje,"PID %d lee contador %s %d bytes\n",getpid(),sContador,nbytes);	
		write(1,mensaje,strlen(mensaje));

		/*convertir el contador a entero*/
		contador=atoi(sContador);

		/*incrementar contador*/
		contador++;

		/*se escribe a salida estandar los mensajes*/
		sprintf(mensaje,"PID %d poner contador a valor %d\n",getpid(), contador);
		write(1,mensaje,strlen(mensaje));	

		/*escribir el resultado al inicio del archivo*/
		if ( lseek(fd,0,SEEK_SET) ){
			perror("Error en lseek");
		}

		sprintf(sContador,"%d\n",contador);

		write(fd,sContador,strlen(sContador));	
	}

	/*cerrar el archivo*/
	close(fd);

}


Este programa al ejecutarse y variando el numero de procesos, lleva
a condiciones de concurrencia.

cat > contador
0


Ejemplo1 3 > log

Al analizar el archivo log, se puede encontrar que un proceso genera
una falta de consistencia en el archivo.

Por ejemplo, al correrlo 

...
PID 2082 lee contador 39 3 bytes
PID 2081 lee contador 39 3 bytes
...

dos procesos leyeron el mismo valor de variable de una forma simultanea.
Esta seccin de cgido se conoce como seccin crtica.

+ Semforos. 

Una tcnica para manipular concurrencia es utilizar semforos.

Un semforo es un nmero entero con dos operaciones atmicas (es decir,
no se puede suspender su ejecucin a la mitad de dichas operaciones), que
se conocen como wait ( down, P o lock) y signal (up, V, unlock).

Cuando un proceso invoca una operacion wait sobre un semforo, el proceso
no puede avanzar hasta que el valor del semforo sea positivo. Si el 
valor es positivo, decrementa el valor del semforo.

La operacin signal es la encargada de incrementar el valor de un semforo.

Un semforo se utiliza para controlar el acceso a secciones crticas, 
asegurando que SOLO un proceso ejecute dicha seccion crtica, mientras los
otros esperan.

En el ejemplo pasado, se puede poner un semforo en la siguiente rea
de cdigo.


...

	/* INSERTAR CODIGO PARA INICIALIZAR EL SEMAFORO A VALOR 1*/
	while ( contador < 99){
		int nbytes;	

		/*AQUI INSERTAR CODIGO PARA INDICAR UNA OPERACION
		WAIT (DOWN) SOBRE EL SEMAFORO, QUE LO LLEVA A VALOR DE 0
		SI DOS PROCESOS LLEGAN AL MISMO TIEMPO, EL KERNEL SE
		ENCARGA DE ASEGURAR QUE SOLO UNO DE ELLOS DECREMENTE 
		EL VALOR DEL SEMAFORO Y EL OTRO SE QUEDE BLOQUEADO 
		HASTA QUE SEA POSITIVO*/
	
		/*poner el descriptor al inicio del archivo*/	
		if ( lseek(fd,0,SEEK_SET) ){
			perror("Error en lseek");
		}
		

		/*leer el archivo*/
		nbytes= read(fd,sContador,sizeof(sContador));	

		if (nbytes < 0)  {
			perror("Error al leer el archivo");
			exit(2);
		}

		/*quitar \n y poner un nulo*/
		sContador[nbytes-1]=0;

		/*se escribe a salida estandar los mensajes*/
		sprintf(mensaje,"PID %d lee contador %s %d bytes\n",getpid(),sContador,nbytes);	
		write(1,mensaje,strlen(mensaje));

		/*convertir el contador a entero*/
		contador=atoi(sContador);

		/*incrementar contador*/
		contador++;

		/*se escribe a salida estandar los mensajes*/
		sprintf(mensaje,"PID %d poner contador a valor %d\n",getpid(), contador);
		write(1,mensaje,strlen(mensaje));	

		/*escribir el resultado al inicio del archivo*/
		if ( lseek(fd,0,SEEK_SET) ){
			perror("Error en lseek");
		}

		sprintf(sContador,"%d\n",contador);

		write(fd,sContador,strlen(sContador));	

		/*AQUI INSERTAR CODIGO PARA INDICAR UNA OPERACION
		SIGNAL (UP) SOBRE EL SEMAFORO, QUE LO LLEVA A VALOR DE 1
		NUNCA DOS PROCESOS LLEGAN AL MISMO TIEMPO. LOS PROCESOS
		BLOQUEADOS SE DESPIERTAN*/

	}
....

En el punto anterior se est utilizando un semforo binario.

Para poder implantar esta tcnica, es necesario recurrir a las llamadas
del sistema opertivo UNIX.

+ Llamadas UNIX para manipular semforos.

La manera como UNIX implanta los semforos es utilizando el concepto
de un conjunto de semforos.

El kernel de UNIX es el encargado de crear un conjunto de semforos. Dicho
conjunto puede ser desde un semforo hasta el nmero mximo de semforos
permitido por el sistema operativo. Un conjunto de semforos se puede
concebir como un arreglo de semforos.

Cuando un semforo es creado, el kernel le da un valor inicial de 0.

Es vlido que un conjunto de semforos sea utilizado por distintos procesos.
Es por esto, que al kernel, se le debe indicar una etiqueta para nombrar
al conjunto de semforos.

La llamada al sistema operativo (no POSIX) que permite crear u obtener
una referencia a un conjunto de semforos es semget(2):

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

Esta llamada crea un conjunto de semforos. El primer parmetro es la
etiqueta con la que el kernel conoce al semforo y es un numero entero; el
segundo parmetro indica el nmero de semforos que se incluyen en el
conjunto de semforos y el ltimo parmetro son las banderas para obtener
este semforo, si el semforo no existe se puede indicar que sea creado y los
permisos.
Los permisos de un semforo son de lectura y alteracin, funciona de la
misma manera que el concepto de permisos en archivos, por ejemplo 0666 indica
un permiso de lectura y alteracin para el propietario, grupo y los dems usuarios. La bandera IPC_CREAT permite indicar que el conjunto de 
semforos se debe crear. 
Si el tercer parmetro de semget es 0, solo permite que el proceso
obtenga un referencia al conjunto de semforos.

Esta llamada regresa un nmero entero, mayor que cero, si tuvo xito e 
indica un nmero con el cual se podr referenciar el conjunto de 
semforos dentro del proceso. En caso de que exista un error (no se 
tiene permisos de acceso).

El siguiente ejemplo muestra la creacin de un conjunto de semforos,
de tamao 1.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


#define SEMKEY 123456L


main() {

	int semid;

	/*crear un conjunto de semaforo de tamanno 1, 
	con permiso de lectura y asignacion para cualquier usuario
	*/
	semid=semget(SEMKEY, 1, IPC_CREAT | 0666);

	/*checar si existe algun error*/
	if (semid < 0) {
		perror("Error al crear el semaforo");
		exit(1);
	}
	printf("Semaforo creado\n");

	exit(0);
}

Al ejecuta este programa

Ejemplo2
Semaforo creado

se puede checar que este semforo existe, con el comando ipcs -s

ipcs -s

IPC status from <running system> as of Wed May  5 19:32:41 1999
T         ID      KEY        MODE        OWNER    GROUP
Semaphores:
s          0   0x0001e240 --ra-ra-ra-      gus    other


Con este comando se pueden identificar todos los semforos activos en
el sistema operativo.

Para remover el semforo del sistema operativo, se utiliza la llamada
semctl(2):

     #include <sys/types.h>
     #include <sys/ipc.h>
     #include <sys/sem.h>

     int semctl(int semid, int semnum, int cmd, ...);

este recibe como parmetros :
El identificador del semforo, dado por la llamada semget
El nmero de semaforo del conjunto sobre el cual se aplica el comando.
El comando  a aplicar, que en esta caso es IPC_RMID
El cuarto parmetro es opcional, dependiendo del comando a aplicar.

El siguiente ejemplo muestra un programa que destruye al conjunto de semaforos
creado en el Ejemplo2

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


#define SEMKEY 123456L


main() {

	int semid;

	/*crear un conjunto de semaforo de tamanno 1, 
	con permiso de lectura y asignacion para cualquier usuario
	*/
	semid=semget(SEMKEY, 1,0);

	/*checar si existe algun error*/
	if (semid < 0) {
		perror("Error al obtener  el conjunto de semaforo");
		exit(1);
	}

	/* se destruye el semaforo, utilizando la llamada semctl,
	el primer parametro es identificador del conjunto de semaforos,
	el segundo indica el numero de semaforo sobre el cual se aplica
	el comando, y el tercer parametro indica que se debe remover
	el conjunto de semaforos
	*/
	if (semctl(semid,0,IPC_RMID) < 0 ) {
		perror("Error al destruir el conjunto de semaforo");
		exit(1);
	}
	printf("Semaforo destruido\n");
	exit(0);
}

Al correr este ejemplo

Ejemplo3
Semaforo destruido

y checando con el comando

ipcs -s
IPC status from <running system> as of Wed May  5 19:44:19 1999
T         ID      KEY        MODE        OWNER    GROUP
Semaphores:

se muestra que no existe un semforo.

En UNIX se puede utilizar el comando ipcsrm para esta actividad.

La llamada semctl permite indicar un valor para el semaforo, obtener su
valor.

El siguiente ejemplo lo muestra.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


#define SEMKEY 123456L

/* se declara esta union para la llamada semctl*/
union semun {
       int val;
       struct semid_ds *buf;
       ushort *array;
 };

main() {

	int semid;
	union semun semctl_arg;

	/*crear un conjunto de semaforo de tamanno 1, 
	con permiso de lectura y asignacion para cualquier usuario
	*/
	semid=semget(SEMKEY, 1,IPC_CREAT|0666);

	/*checar si existe algun error*/
	if (semid < 0) {
		perror("Error al obtener  el conjunto de semaforo");
		exit(1);
	}


	/*leer el valor con el comando GETVAL*/	
	semctl_arg.val=semctl(semid,0,GETVAL) ;
	if (semctl_arg.val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}

	printf("Valor actual del semaforo %d\n",semctl_arg.val);

	/*dar un valor de uno al semaforo*/
	semctl_arg.val= 1;
	if (semctl(semid,0,SETVAL,semctl_arg) < 0) {
		perror("Error al asignar un valor");
		exit(1);
	}

	/*leer el valor con el comando GETVAL*/	
	semctl_arg.val= 0;
	semctl_arg.val=semctl(semid,0,GETVAL) ;
	if (semctl_arg.val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	printf("Valor actual del semaforo %d\n",semctl_arg.val);
	
	/* se destruye el semaforo, utilizando la llamada semctl,
	el primer parametro es identificador del conjunto de semaforos,
	el segundo indica el numero de semaforo sobre el cual se aplica
	el comando, y el tercer parametro indica que se debe remover
	el conjunto de semaforos
	*/
	if (semctl(semid,0,IPC_RMID) < 0 ) {
		perror("Error al destruir el conjunto de semaforo");
		exit(1);
	}
	printf("Semaforo destruido\n");
	exit(0);
}

Ejemplo3.1
Valor actual del semaforo 0
Valor actual del semaforo 1
Semaforo destruido

La llamada que permite modificar el valor de un semforo, pero de una
forma atmica, es la llamada semop(2).

     #include <sys/types.h>
     #include <sys/ipc.h>
     #include <sys/sem.h>

     int semop(int semid, struct sembuf *sops, size_t nsops);

Esta llamada recibe como parmetros lo siguiente:

El identificador del conjunto de semforos,
La direccin de la primera posicin de un arreglo de estructura sembuf.
El nmero de operaciones (o tamao del arreglo) a aplicar sobre este
conjunto.

La estructura de datos sembuf tiene la siguiente descripcion.

struct sembuf {
          short   sem_num;    /* semaphore number */
          short   sem_op;     /* semaphore operation */
          short   sem_flg;    /* operation flags */
	
}

El primer campo indica a que elemento del conjunto de semforos se le aplican
las operaciones. 
El segundo campo indica el tipo de operacion a aplicar sobre el elemento del
semforo, cumpliendo las siguientes reglas:

Si sem_op es positivo o negativo implica que se debe alterar el valor
del semforo.
Si sem_op es cero, indica lectura sobre el semforo.
Las siguientes condiciones se aplican si:

+ Si el valor es positivo, el semforo ve incrementado su valor por lo indicado
en sem_op.

+ Si sem_op es cero y el valor del semforo es cero, retorna de inmediato
de la llamada.
+ Si sem_op es cero y el valor de semforo NO ES cero, suspende la ejecucin
del proceso. Esto es, el proceso se queda esperando hasta que el semforo
tenga valor 0.
+ Si sem_op es negativo y el valor del semaforo es igual o mayor 
al valor absoluto de sem_op, retorna de la llamada, de lo contrario, se
queda esperando hasta que esta condicion se de.


El tercer campo indica algunas banderas de control de la operacin, que afecta
el comportamiento de la operacin a realizar.

El siguiente ejemplo muestra el uso de esta llamada, donde un solo proceso
entra de forma nica a una seccion de cdigo.
La lgica de este programa es:

+ Se crea un conjunto de semforos, con un solo miembro.
+ Se espera que el semforo obtenga un valor 0 y se incrementa en una unidad.
+ Se entra a la seccin crtica.
+ Se decrementa el valor del semforo.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


#define SEMKEY 123456L


main() {

	int semid;
	int pid;
	int val;
	int i;
	/*definir las operaciones sobre semaforos*/
	struct sembuf semop1[2], semop2;
	char buffer[128];

	/*crear un conjunto de semaforo de tamanno 1, 
	con permiso de lectura y asignacion para cualquier usuario
	*/
	semid=semget(SEMKEY, 1,IPC_CREAT|0666);

	/*checar si existe algun error*/
	if (semid < 0) {
		perror("Error al obtener  el conjunto de semaforo");
		exit(1);
	}

	/*La primera operacion es :
		Leer el semaforo y esperar hasta que sea 0
			E
		Incrementar el valor del semaforo
	*/	
	semop1[0].sem_num=0;
	semop1[0].sem_op=0 ;
	semop1[0].sem_flg=0;
	semop1[1].sem_num=0;
	semop1[1].sem_op=1;
	semop1[1].sem_flg=0;
	
	/*La segunda operacion es decrementar el valor del semaforo
	en una unidad*/
	semop2.sem_num=0;
	semop2.sem_op=-1 ;
	semop2.sem_flg=0;
	
		
	for (i=0;i<2;i++) {
		pid=fork();
		
		if (pid < 0) {
			perror("Error al crear nuevo proceso");
			exit(2);
		}
	}

	/*leer el valor con el comando GETVAL*/	
	val=semctl(semid,0,GETVAL) ;
	if (val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	sprintf(buffer,"PID %d Valor actual del semaforo %d\n",getpid(),val);
	write(1,buffer,strlen(buffer));
	/*efectuar la operacion 1, se queda bloqueda si el proceso
	encuentra un valor del semaforo diferente de 0*/
	sprintf(buffer,"PID  %d aplicando operacion1\n",getpid());
	write(1,buffer,strlen(buffer));
	semop(semid, &semop1[0], 2);

	sprintf(buffer,"PID %d entrando a seccion critica\n",getpid());
	/*leer el valor con el comando GETVAL*/	
	val=semctl(semid,0,GETVAL) ;
	if (val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	sprintf(buffer,"PID %d entrando a seccion critica\n",getpid());
	write(1,buffer,strlen(buffer)); 


	sprintf(buffer,"PID %d Valor actual del semaforo %d\n",getpid(),val);
	write(1,buffer,strlen(buffer));

	sprintf(buffer,"PID %d saliendo de seccion critica\n",getpid());
	write(1,buffer,strlen(buffer));

	/*decrementar el valor del semaforo, despierta a los
	demas procesos*/	
	sprintf(buffer,"PID  %d aplicando operacion2\n",getpid());
	write(1,buffer,strlen(buffer));
	semop(semid, &semop2, 1);

	/*leer el valor con el comando GETVAL*/	
	val=semctl(semid,0,GETVAL) ;
	if (val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	sprintf(buffer,"PID %d Valor actual del semaforo %d\n",getpid(),val);
	write(1,buffer,strlen(buffer));

	
	exit(0);
}

Si las llamadas de semop no son bien aplicadas, pueden llevar a un
bloqueo de otros procesos, en el siguiente ejemplo, derivado del anterior,
el proceso nunca decrementa el valor del semaforo.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


#define SEMKEY 123456L


main() {

	int semid;
	int pid;
	int val;
	int i;
	/*definir las operaciones sobre semaforos*/
	struct sembuf semop1[2], semop2;
	char buffer[128];

	/*crear un conjunto de semaforo de tamanno 1, 
	con permiso de lectura y asignacion para cualquier usuario
	*/
	semid=semget(SEMKEY, 1,IPC_CREAT|0666);

	/*checar si existe algun error*/
	if (semid < 0) {
		perror("Error al obtener  el conjunto de semaforo");
		exit(1);
	}

	/*La primera operacion es :
		Leer el semaforo y esperar hasta que sea 0
			E
		Incrementar el valor del semaforo
	*/	
	semop1[0].sem_num=0;
	semop1[0].sem_op=0 ;
	semop1[0].sem_flg=0;
	semop1[1].sem_num=0;
	semop1[1].sem_op=1;
	semop1[1].sem_flg=0;
	
	/*La segunda operacion es decrementar el valor del semaforo
	en una unidad*/
	semop2.sem_num=0;
	semop2.sem_op=-1 ;
	semop2.sem_flg=0;
	
		
	for (i=0;i<2;i++) {
		pid=fork();
		
		if (pid < 0) {
			perror("Error al crear nuevo proceso");
			exit(2);
		}
	}

	/*leer el valor con el comando GETVAL*/	
	val=semctl(semid,0,GETVAL) ;
	if (val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	sprintf(buffer,"PID %d Valor actual del semaforo %d\n",getpid(),val);
	write(1,buffer,strlen(buffer));
	/*efectuar la operacion 1, se queda bloqueda si el proceso
	encuentra un valor del semaforo diferente de 0*/
	sprintf(buffer,"PID  %d aplicando operacion1\n",getpid());
	write(1,buffer,strlen(buffer));
	semop(semid, &semop1[0], 2);

	sprintf(buffer,"PID %d entrando a seccion critica\n",getpid());
	/*leer el valor con el comando GETVAL*/	
	val=semctl(semid,0,GETVAL) ;
	if (val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	sprintf(buffer,"PID %d entrando a seccion critica\n",getpid());
	write(1,buffer,strlen(buffer)); 

	/*ciclo infinito, nunca termina*/
	while (1);

	sprintf(buffer,"PID %d Valor actual del semaforo %d\n",getpid(),val);
	write(1,buffer,strlen(buffer));

	sprintf(buffer,"PID %d saliendo de seccion critica\n",getpid());
	write(1,buffer,strlen(buffer));

	/*decrementar el valor del semaforo, despierta a los
	demas procesos*/	
	sprintf(buffer,"PID  %d aplicando operacion2\n",getpid());
	write(1,buffer,strlen(buffer));
	semop(semid, &semop2, 1);

	/*leer el valor con el comando GETVAL*/	
	val=semctl(semid,0,GETVAL) ;
	if (val < 0) {
		perror("Error al obtener un valor");
		exit(1);
	}
	sprintf(buffer,"PID %d Valor actual del semaforo %d\n",getpid(),val);
	write(1,buffer,strlen(buffer));

	
	exit(0);
}

PID 831 Valor actual del semaforo 0
PID  831 aplicando operacion1
PID 831 entrando a seccion critica
PID 830 Valor actual del semaforo 1
PID  830 aplicando operacion1
PID 832 Valor actual del semaforo 1
PID  832 aplicando operacion1
PID 829 Valor actual del semaforo 1
PID  829 aplicando operacion1

Se recomienda remover el semaforo despues de ejecutar este programa, con el
comando ipcsrm


+ Semforo binario.

Un semaforo binario es aquel en el cual el semaforo toma un valor de 0 o 1.
Si el semaforo tiene un valor 0, se bloquean los procesos que lo invoquen,
si es 1, pueden avanzar.



Para implantar esto en UNIX.
+ Se debe aplicar una operacion 0 sobre un semaforo de valor igual o mayor a
0 y si es mayor a 0 bloquerse, sino incrementar en una unidad el valor
del semaforo.
+ Se debe aplicar una operacion -1 sobre el semaforo.

Se construira un mdulo u objeto que define un semforo binario.

El siguiente ejemplo muestra un archivo de encabezado y la corresponidente
implantacion de cada rutina.

/*definicion de un objeto semaforo binario*/

/*atributos o propiedades del objeto*/

struct semBinario {
	int key; /*llave como se conoce en el kernel*/
	int semId; /*identificador del semaforo*/
};

/*crear un semaforo dentro del kernel*/
extern void creaSemaforoBinario(struct semBinario * sembin);
/*subir al semaforo*/
extern int up(struct semBinario * sembin);
/*bajar al semaforo*/
extern int down(struct semBinario * sembin);

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#include "Ejemplo4.h"



void creaSemaforoBinario(struct semBinario * sembin) {
	/*obtiene la llave*/
	int key= sembin->key;
	/*una llave menor o igual a cero no es aceptable*/
	if (key <= 0 ){
		sembin->semId= -1;
		return;
	}
	sembin->semId= semget(sembin->key, 1, IPC_CREAT | 0666);
}

int down(struct semBinario * sembin) {

	struct sembuf opdown[2];

	/* la operacion a realizar es:*/
	/*leer el valor esperando que sea 0, sino bloquerse*/
	opdown[0].sem_num=0;
	opdown[0].sem_op=0;
	opdown[0].sem_flg=0;
	/*E incrementar en una unidad el semaforo*/
	opdown[1].sem_num=0;
	opdown[1].sem_op=1;
	opdown[1].sem_flg=0;

	return semop(sembin->semId, &opdown[0],2);
}

int up(struct semBinario * sembin) {
	struct sembuf opup;

	/*la operacion a realizar es:
	decrementar en una unidad el semaforo si y solo si 
	tiene un valor de 1
	*/
	opup.sem_num=0;
	opup.sem_op=-1;
	opup.sem_flg=0;

	return semop(sembin->semId, &opup,1);


}

Para compilar este modulo, se debe aplicar:

gcc -c Ejemplo4.c

que produce un archivo Ejemplo4.o, que posteriormente ser utilizado para
enlazado con un programa que lo utilice.


+ Problemas de sincronizacin.

El primer problema que se va a resolver es el Ejemplo1, que es un
problema de seccin crtica.

En este problema se usa un semaforo binario, el cual se coloca en
la seccin critica, que es la lectura, incremento y escritura del contador.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "Ejemplo4.h"

#define CONTADOR "contador"


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


	int fd; /*descriptor del archivo a leer/escribir*/
	int pid; 
	int contador=0; /*variable contador*/
	int i;
	char sContador[4]; /*contenido del 
				archivo de maximo tres posiciones*/
	char mensaje[256]; /*buffer de mensaje para depurar*/
	int nProcs; /*numero de procesos a arrancar*/
	struct semBinario sem;

	if (argc <=1 ) {
		printf("Ejemplo5 nProcesos\n");
		exit(1);
	}	

	nProcs=atoi(argv[1])-1;

	/*abrir el archivo para lectura y escritura*/

	fd= open(CONTADOR, O_RDWR);

	if (fd < 0) {
		perror("Error al abir el archivo");
		exit(1);
	}

	/*el proceso padre crea el semaforo*/
	sem.key=123456L;
	creaSemaforoBinario(&sem);

	if (sem.semId < 0 ) {
		perror("Error al crear el semaforo");
		exit(2);
	}

	/*crear nProcs */
	for (i=0;i<nProcs;i++) {
		pid=fork();

		if (pid < 0) {
			perror("Error al crear al hijo");
			exit(2);
		} 
	}

	/*padre e hijo ejecutan el mismo codigo*/

	while ( contador < 99){
		int nbytes;	

		sprintf(mensaje,"Proceso %d efectuando down\n",getpid());
		write(1,mensaje,strlen(mensaje));

		/*bajar el semaforo*/
		if ( down(&sem) )
			perror("Error al bajar semaforo");	

		sprintf(mensaje,"Proceso %d concluyendo down\n",getpid());
		write(1,mensaje,strlen(mensaje));

		/*poner el descriptor al inicio del archivo*/	
		if ( lseek(fd,0,SEEK_SET) ){
			perror("Error en lseek");
		}
		

		/*leer el archivo*/
		nbytes= read(fd,sContador,sizeof(sContador));	

		if (nbytes < 0)  {
			perror("Error al leer el archivo");
			exit(2);
		}

		/*quitar \n y poner un nulo*/
		sContador[nbytes-1]=0;

		/*se escribe a salida estandar los mensajes*/
		sprintf(mensaje,"PID %d lee contador %s %d bytes\n",getpid(),sContador,nbytes);	
		write(1,mensaje,strlen(mensaje));

		/*convertir el contador a entero*/
		contador=atoi(sContador);

		/*incrementar contador*/
		contador++;

		/*se escribe a salida estandar los mensajes*/
		sprintf(mensaje,"PID %d poner contador a valor %d\n",getpid(), contador);
		write(1,mensaje,strlen(mensaje));	

		/*escribir el resultado al inicio del archivo*/
		if ( lseek(fd,0,SEEK_SET) ){
			perror("Error en lseek");
		}

		sprintf(sContador,"%d\n",contador);

		write(fd,sContador,strlen(sContador));	

		sprintf(mensaje,"Proceso %d efectuando up\n",getpid());
		write(1,mensaje,strlen(mensaje));

		/*subir al semaforo*/	
		if (up(&sem) < 0)
			perror("Error al subir el semaforo");
		sprintf(mensaje,"Proceso %d concluyendo up\n",getpid());
		write(1,mensaje,strlen(mensaje));
	}

	/*cerrar el archivo*/
	close(fd);

}

Otros problemas clsicos de sincronizacin, son:
+ Lectores y escritores con un Buffer limitado.
+ Los filsofos comensales.

Antes de plantear estos problemas, y su solucin utilizando semforos,
se tiene que explicar otro concepto, memoria compartida.

+ Memoria compartida.

En UNIX, la memoria compartida sirve para que dos o mas procesos compartan
los mismos datos. 

Mientras que con el esquema de tuberias, era posible que dos procesos se
comunicaran datos, implicaba el realizar escritura o lectura de los mismos
al kernel. 

Con la memoria compartida, se puede tener un proceso que lea un archivo
y lo carge a memoria compartida, y de ahi, otro proceso, sin necesidad de
abrir el archivo y copiarlo a memoria, puede accesar la imagen del archivo.

Esto da como ventaja que se optimice procesamiento cooperativo, en el cual
los resultado de dicho procesamiento se escribe a una area comn.

La memoria compartida se puede utilizar para implantar un "cache" de datos.

+ Llamadas al sistema para memoria compartida.

Al igual que semforos, UNIX implanta la memoria compartida como un mecanismo
de IPC.

Un segmento de memoria compartida se almacena en el kernel y tiene un
tamao definido. Dicho segmento es etiquetado con un nmero dentro del kernel.

Para crear un segmento de memoria compartida se utiliza la llamada semget(2):

     #include <sys/types.h>
     #include <sys/ipc.h>
     #include <sys/shm.h>

     int shmget(key_t key, size_t size, int shmflg);

Esta recibe tres parmetros:
La llave o etiqueta dentro del kernel.
El tamao del segmento.
Una bandera, si esta bandera contiene IPC_CREAT, crea el segmento y
se puede combinar con los permisos de lectura y escritura para el propietario,
grupo y otros. Si es 0, solo se conecta a memoria compartida.

Esta llamada retorna negativo si existe algn error, o un nmero positivo
que es igual al identificador de memoria compatida.

El siguiente ejemplo crea un segmento de archivo de memoria compartida
y de tamao igual al archivo /etc/passwd.


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

main() {

	int shid;
	int fd;
	int sz;

	/*abrir el archivo*/
	fd=open("/etc/passwd",O_RDONLY);


	
	if (fd < 0 ) {
		perror("no se puede abrir /etc/passwd");
		exit(1);
	}

	/*Calcular el tamanno del archivo*/
	if ( (sz=lseek(fd,0,SEEK_END)) < 0) {
		perror("Error al calcular tamanno del archivo");
		exit(2);
	}


	/*crear el segmento de memoria compartida*/
	shid= shmget(123456L,sz,IPC_CREAT | 0666); 

	if (shid < 0) {
		perror("Error al crear el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);
	exit(0);
}

Ejemplo6
Segmento 0 con tamanno 530

y con el comando

ipcs -m
IPC status from <running system> as of Wed May  5 21:58:33 1999
T         ID      KEY        MODE        OWNER    GROUP
Shared Memory:
m          0   0x0001e240 --rw-rw-rw-      gus    other

Para remover un segmento de memoria compartida se puede utilizar
la llamada shmctl(2), o el comando ipcrm

ipcrm -m 0

La llamada que permite leer o escribir a un segmento de memoria es
shmat(2)

     #include <sys/types.h>
     #include <sys/shm.h>

     void *shmat(int shmid, const void *shmaddr, int shmflg);
los parametros de esta llamada son:
El identificador de memoria compartida, generado por shmget
La direccion a partir de la cual se quiere indexar el segmento, generalmente
es (void *) 0.
Banderas

Esta llamada retorna un apuntador de tipo (void *), el cual se puede manipular
ya sea realizando lectura o escritura sobre l. Si hay algn error, se
retorna un negativo.

Para desconectarse de un segmento de memoria se usa la llamada shmdt(2)

  int shmdt(char *shmaddr);

El siguiente ejemplo, guarda el archivo /etc/passwd en un segmento de memoria
compartida.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

main() {

	int shid;
	int fd;
	int sz;
	char * buffer;
	int nbytes, totbytes;

	/*abrir el archivo*/
	fd=open("/etc/passwd",O_RDONLY);


	
	if (fd < 0 ) {
		perror("no se puede abrir /etc/passwd");
		exit(1);
	}

	/*Calcular el tamanno del archivo*/
	if ( (sz=lseek(fd,0,SEEK_END)) < 0) {
		perror("Error al calcular tamanno del archivo");
		exit(2);
	}


	/*crear el segmento de memoria compartida*/
	shid= shmget(123456L,sz,IPC_CREAT | 0666); 

	if (shid < 0) {
		perror("Error al crear el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);

	buffer= (char *)shmat(shid,(void *) 0, 0);

	if (buffer < 0) {
		perror("Error en shmat");
		exit(4);
	}

	/*posicionar el archivo al inicio*/
	lseek(fd,0,SEEK_SET);

	/*cargar el archivo*/

	totbytes=0;	
	while (1) {

		nbytes = read(fd,buffer,sz);

		if (nbytes < 0) {
			perror("Error en read");
			exit(5);
		}

		printf("Leyendo %d bytes \n",nbytes);
		totbytes+=nbytes;
		if ( totbytes >= sz) 
			break;

	}

	printf("Archivo cargado\n");

	if (shmdt(buffer) < 0) {
		perror("Error en shmdt");
		exit(6);
	}

	close(fd);
	exit(0);
}

El siguiente ejemplo lee el segmento de memoria, cargado previamente
en el punto pasado.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

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

	int shid;
	int i;
	char * buffer;
	int sz;

	if (argc<=1) {
		printf("Ejemplo8 tamanno");
		exit(2);
	}

	/*el tamanno del segmento se obtiene como argumento*/
	sz=atoi(argv[1]);
	
	/*conectarse el segmento de memoria compartida*/
	shid= shmget(123456L,sz,0); 

	if (shid < 0) {
		perror("Error al obtener el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);

	/*obtener el apuntador al segmento*/
	buffer= (char *)shmat(shid,(void *) 0, 0);

	if (buffer < 0) {
		perror("Error en shmat");
		exit(4);
	}

	/*leer el buffer, caracter por caracter*/
	i=0;
	while (i<sz) {

		if (buffer[i]==0)
			break;
		printf("%c",buffer[i]);
		i++;
	}

	printf("\nArchivo cargado\n");

	if (shmdt(buffer) < 0) {
		perror("Error en shmdt");
		exit(6);
	}

	exit(0);
}

Para correr este programa (en funcion del tamanno del archivo de passwd)

Ejemplo8 530


+ De retorno a los problemas de sincronizacion.

Con memoria compartida se puede implantar la solucin a ciertos problemas
de sincronizacin.

Uno de ellos es el problema de un productor/consumidor con un buffer
limitado.

El problema consiste en un proceso productor escribe datos a un buffer
de un tamao determinado. Mientras tanto el consumidor lee en ese mismo
buffer y remueve el dato. 
El siguiente ejemplo muestra este concepto, donde el proceso productor
genera todo el alfabeto y el consumidor lee dicho alfabeto y coloca un 
nulo. Si el productor llega a la letra z, vuelve a empezar.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

main() {

	int shid;
	char * buffer;
	char ch;
	int i;
	int sz= (int)('z'-'a');


	/*crear el segmento de memoria compartida*/
	shid= shmget(123456L,sz,IPC_CREAT | 0666); 

	if (shid < 0) {
		perror("Error al crear el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);

	buffer= (char *)shmat(shid,(void *) 0, 0);

	if (buffer < 0) {
		perror("Error en shmat");
		exit(4);
	}

	while (1) {

		i=0;
		for (ch='a';ch<='z';ch++) {
			printf("Escribiendo %c en celda %d\n",ch,i);
			buffer[i]=ch;
			i++;
		}
	}

	exit(0);
}

El siguiente ejemplo es el consumidor. Este consumidor lee un caracter
y pone un nulo

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

main() {

	int shid;
	char * buffer;
	char ch;
	int i;
	int sz= (int)('z'-'a');


	/*obtener el segmento de memoria compartida*/
	shid= shmget(123456L,sz, 0); 

	if (shid < 0) {
		perror("Error al obtener el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);

	buffer= (char *)shmat(shid,(void *) 0, 0);

	if (buffer < 0) {
		perror("Error en shmat");
		exit(4);
	}

	while (1) {

		i=0;
		for (ch='a';ch<='z';ch++) {
			if (buffer[i]!=0) {
				printf(" %c",buffer[i]);
			} else {
			/*si en esta celda aun no se produce nada*/
				printf("Error en lectura");
				/*consumo no valido*/
				break;
			}
			/*remover*/
			buffer[i]=0;
			i++;
		}
		printf("\n");
	}

	exit(0);
}

El problema con este programa es que se puede dar el caso en el
cual el consumidor corre mas rapido que el producto, provocando que este
encuentre frecuentemente celdas sin producir.

Esto lleva a la necesidad de sincronizar al proceso productor y consumidor.

Para esto, se identifica lo siguiente:
+ Existe una seccin crtica entre el proceso producto y consumidor,
y que consiste en la escritura y lectura a la celda.
+ El consumidor debe dejar de consumir cuando vacie el buffer y esperar
a que el productor por lo menos llene una celda del buffer.

Para obtener la solucin a este problema, se deben utilizar semaforos.
Para el control de la seccin crtica, se puede utilizar un semforo binario.
Pero el control del nmero de elementos en el buffer, ser controlador por
un semforo que toma valores entre 0 y N.

Para esto, se debe implantar un semforo que tome valores entre 0 y N.

El siguiente cdigo muestra la implantacion de este semforo:

/*definicion de un objeto semaforo binario*/

/*atributos o propiedades del objeto*/

struct semaforo {
	int key; /*llave como se conoce en el kernel*/
	int semId; /*identificador del semaforo*/
	int valorinicial; /*valor del semaforo*/
};

/*crear un semaforo dentro del kernel*/
extern void creaSemaforo(struct semaforo * sem);
/*abrir semaforo*/
extern void abreSemaforo(struct semaforo * sem );
/*subir al semaforo*/
extern int V(struct semaforo * sem);
/*bajar al semaforo*/
extern int P(struct semaforo * sem);

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#include "Ejemplo11.h"

/* se declara esta union para la llamada semctl*/
union semun {
       int val;
       struct semid_ds *buf;
       ushort *array;
 };


void creaSemaforo(struct semaforo * sem) {
	/*obtiene la llave*/
	int key= sem->key;
	int valorinicial= sem->valorinicial;
	
	/*una llave menor o igual a cero no es aceptable*/
	if (key <= 0 ){
		sem->semId= -1;
		return;
	}

	/*crear el semaforo*/
	sem->semId= semget(sem->key, 1, IPC_CREAT | 0666);
	
	/*indicar el valor del semaforo
		OJO esta rutina solo debe invocarse por SOLO
		un proceso
	*/
	if (sem->semId >= 0) {
		union semun semctl_arg;

		/*dar el valor inicial al semaforo*/
        	semctl_arg.val= sem->valorinicial;
        	if (semctl(sem->semId,0,SETVAL,semctl_arg) < 0) {
               		perror("Error al asignar un valor");
			sem->semId=-2;
               	}
        }
				
}

void abreSemaforo(struct semaforo * sem ) {
	int key= sem->key;

	/*una llave menor o igual a cero no es aceptable*/
	if (key <= 0 ){
		sem->semId= -1;
		return;
	}
	/*abrir el semaforo*/
	sem->semId= semget(sem->key, 1, 0);
}
int P(struct semaforo * sem) {

	struct sembuf opdown[1];

	/* la operacion a realizar es:*/
	/* decrementar en una unidad el semaforo
	con la posibilidad de esperar hasta que el valor del
	semaforo sea igual o mayor al valor absoluto de sem_op*/
	opdown[0].sem_num=0;
	opdown[0].sem_op=-1;
	opdown[0].sem_flg=0;

	return semop(sem->semId, &opdown[0],1);
}

int V(struct semaforo * sem) {
	struct sembuf opup;

	/*la operacion a realizar es:
	incrementar en una unidad el semaforo si y solo si 
	tiene un valor de 1 o superior
	*/
	opup.sem_num=0;
	opup.sem_op=1;
	opup.sem_flg=0;

	return semop(sem->semId, &opup,1);


}

Para mostrar el uso de este semforo, el siguiente programa asigna un valor
de 10 unidades al semaforo. Este se queda bloqueado al 11 intento.

#include "Ejemplo11.h"

#define N 10

main() {


	struct semaforo sem;
	int i;
	char buffer[128];

	sem.key=654321L;
	sem.valorinicial=N;

	creaSemaforo(&sem);	

	if (sem.semId <0) {
		printf("error al crear semaforo\n");
		exit(1);
	}

	i=1;
	while (i) {
		sprintf(buffer,"Intentando bajar semaforo por %d vez\n",i);	
		write(1,buffer,strlen(buffer));
		if ( P(&sem) <0){
			perror("Error en P");
			exit(2);
		}
		i++;
	}
}


Ejemplo12
Intentando bajar semaforo por 1 vez
Intentando bajar semaforo por 2 vez
Intentando bajar semaforo por 3 vez
Intentando bajar semaforo por 4 vez
Intentando bajar semaforo por 5 vez
Intentando bajar semaforo por 6 vez
Intentando bajar semaforo por 7 vez
Intentando bajar semaforo por 8 vez
Intentando bajar semaforo por 9 vez
Intentando bajar semaforo por 10 vez
Intentando bajar semaforo por 11 vez

El proceso se queda bloqueado al 11 intento, ya que el valor del semaforo
es 0, que es menor al valor absoluto de la operacion de decremento.

El siguiente programa incrementa al mismo semaforo una cantidad dada de
veces.

#include "Ejemplo11.h"

#define N 10

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


	struct semaforo sem;
	int i,n;
	char buffer[128];

	if (argc <=1) {
		printf("Ejemplo13 n\n");
		exit(1);
	}

	n= atoi(argv[1]);

	sem.key=654321L;

	abreSemaforo(&sem);	

	if (sem.semId <0) {
		printf("error al crear semaforo\n");
		exit(1);
	}

	i=1;
	while (i<=n) {
		sprintf(buffer,"Subiendo semaforo por %d vez\n",i);	
		write(1,buffer,strlen(buffer));
		if ( V(&sem) < 0 ) {
			perror("error al subir el semaforo");
			exit(2);
		}
		i++;
	}
}


Ejemplo13 1
Subiendo semaforo por 1 vez

Este programa permite que el proceso del Ejemplo12 pueda decrementar al
semaforo en el 11 intento.

Bajo estas condiciones, es posible solucionar el problema del productor/
consumidor.

A continuacin se muestra el cdigo:

#include "Ejemplo11.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

/*consturir todos los mecanismos de concurrencia para el problema
del productor/consumidor*/

main() {

	struct semaforo mutex;
	struct semaforo full;
	struct semaforo empty;
	int sz= (int)('z'-'a');
	int shid;

	mutex.key=123456;
	mutex.valorinicial=1;
	creaSemaforo(&mutex);

	if ( mutex.semId < 0) {
		printf("Error al crear el mutex\n");
		exit(1);
	}

	empty.key=333;
	empty.valorinicial=(int) ('z'-'a');
	creaSemaforo(&empty);

	if ( empty.semId < 0) {
		printf("Error al crear el semaforo empty\n");
		exit(1);
	}


	full.key=444;
	full.valorinicial=0;
	creaSemaforo(&full);

	if ( full.semId < 0) {
		printf("Error al crear el semaforo full\n");
		exit(1);
	}

        /*crear el segmento de memoria compartida*/
        shid= shmget(123456L,sz,IPC_CREAT | 0666);

        if (shid < 0) {
                perror("Error al crear el segmento de memoria compartida");
                exit(3);
        }

        printf("Segmento %d con tamanno %d\n",shid,sz);



}

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "Ejemplo4.h"
#include "Ejemplo11.h"

main() {

	int shid;
	char * buffer;
	char ch;
	int i;
	int sz= (int)('z'-'a');
        struct semaforo mutex;
        struct semaforo empty;
        struct semaforo full;

	/*conectar al segmento de memoria compartida*/
	shid= shmget(123456L,sz, 0); 

	if (shid < 0) {
		perror("Error al crear el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);

	buffer= (char *)shmat(shid,(void *) 0, 0);

	if (buffer < 0) {
		perror("Error en shmat");
		exit(4);
	}

	mutex.key=123456;
	abreSemaforo(&mutex);

	if (mutex.semId < 0) {
		perror("Error al abrir mutex");
		exit(5);
	}



	empty.key=333;
	abreSemaforo(&empty);

	if (empty.semId < 0) {
		perror("Error al abrir empty");
		exit(6);
	}

	full.key=444;
	abreSemaforo(&full);

	if (full.semId < 0) {
		perror("Error al abrir full");
		exit(7);
	}

	while (1) {

		i=0;
		for (ch='a';ch<='z';ch++) {
			printf("Escribiendo %c en celda %d\n",ch,i);

			if ( P(&empty) < 0 ) {
				perror("Error en empty.P");
				exit(8);
			}


			if ( P(&mutex) < 0 ) {
				perror("Error en mutex.P");
				exit(9);
			}

			buffer[i]=ch;
			i++;

			if ( V(&mutex) < 0 ) {
				perror("Error en mutex.V");
				exit(10);
			}


			if ( V(&full) < 0 ) {
				perror("Error en full.V");
				exit(11);
			}

		}
	}

	exit(0);
}

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "Ejemplo4.h"
#include "Ejemplo11.h"

main() {

	int shid;
	char * buffer;
	char ch;
	int i;
	int sz= (int)('z'-'a');
	struct semaforo mutex;
        struct semaforo empty;
        struct semaforo full;


	/*obtener el segmento de memoria compartida*/
	shid= shmget(123456L,sz, 0); 

	if (shid < 0) {
		perror("Error al obtener el segmento de memoria compartida");
		exit(3);
	}

	printf("Segmento %d con tamanno %d\n",shid,sz);

	buffer= (char *)shmat(shid,(void *) 0, 0);

	if (buffer < 0) {
		perror("Error en shmat");
		exit(4);
	}

        mutex.key=123456;
        abreSemaforo(&mutex);

        if (mutex.semId < 0) {
                perror("Error al abrir mutex");
                exit(5);
        }



        empty.key=333;
        abreSemaforo(&empty);

        if (empty.semId < 0) {
                perror("Error al abrir empty");
                exit(6);
        }

        full.key=444;
        abreSemaforo(&full);

        if (full.semId < 0) {
                perror("Error al abrir full");
                exit(7);
        }

	
	while (1) {

		i=0;
		for (ch='a';ch<='z';ch++) {

			if ( P(&full) < 0 ) {
                                perror("Error en full.P");
                                exit(11);
                        }


			if ( P(&mutex) < 0 ) {
                                perror("Error en mutex.P");
                                exit(11);
                        }

			if (buffer[i]!=0) {
				printf(" %c",buffer[i]);
			} else {
			/*si en esta celda aun no se produce nada*/
				printf("Error en lectura de celda %d",i);
				/*consumo no valido*/
				break;
			}
			/*remover*/
			buffer[i]=0;
			i++;

                        if ( V(&mutex) < 0 ) {
                                perror("Error en mutex.V");
                                exit(10);
                        }


                        if ( V(&empty) < 0 ) {
                                perror("Error en full.V");
                                exit(11);
                        }
			
		}
		printf("\n");
	}

	exit(0);
}

Ejercicios.

1. Documenta la solucin del problema del productor/consumidor con buffer
limitado.
2. Investigar el problema de concurrencia de lectores y escrjtores.
3. Implantar el problema no sincronizado de lectores y escritores, utilizando
memoria compartida.
4. Implantar el problema sincronziado de lectores y escritores, utilizando
memoria compartida y semforos.


+ Mensajes.

Otro mecanismo de IPC que UNIX proporciona son las colas de mensajes.


Una cola de mensaje es un repositorio de mensajes del cual, procesos pueden
tomar o poner un mensaje.

En UNIX, las colas de mensajes estn diseadas para que un proceso se
interese por un mensaje de un tipo determinado.

Un mensaje porta informacin, que puede ser de cualquier tipo.

Para depositar un mensaje en la cola de mensajes, esta debe ser previamente
creada por el kernel, y el depositar o tomar mensajes de la cola, es controlado
por el kernel.

Por tanto existen llamadas al sistema para crear colas de mensajes, para
enviar un mensaje y para recibir un mensaje.

Esta forma de comunicacin permite que uno o ms procesos reciban informacin
y que en algn momento se sincronice el acceso a cierto recurso enviando
mensajes a ciertos procesos.

+ Llamadas al sistema para manipular colas de mensajes.

Al igual que semforos y memoria compartida, para cola de mensajes existe
una llamada para crear o conectarse a la cola de mensajes, denominada
msgget(2)

     #include <sys/msg.h>

     int msgget(key_t key, int msgflg);

El primer parmetro es la llave del IPC, que es un numero entero.
El segundo parmetro es un conjunto de banderas, en caso de aplicar una
creacin de la cola de mensajes se debe indicar la bandera IPC_CREAT combinada
con el esquema de permisos (lectura y escritura sobre la cola; en caso de
que la cola ya est creada se debe indicar un 0.
Esta llamada retorna un nmero entero, que indica el identificador de la
cola. Si es negativo, existe un error en esta llamada.

El siguiente ejemplo muestra la creacin de una cola de mensajes.

#include <sys/msg.h>


main() { 


	int qid;

	/*invocar la llamada mssget, para un cola con llave 123456
	y se crea la cola con permisos de lectura y escritura para
	propietario, grupo y otros usuarios*/
	qid= msgget(123456,IPC_CREAT|0666);

	if (qid < 0) {
		perror("Error al crear la cola de mensajes");
		exit(1);
	}

	printf("Cola creada\n");

	exit(0);


}


El correr este programa:

Ejemplo17
Cola creada

ipcs -q 
IPC status from <running system> as of Sun May  9 18:11:00 1999
T         ID      KEY        MODE        OWNER    GROUP
Message Queues:
q          0   0x0001e240 --rw-rw-rw-      gus    other


Para poder remover una cola, se puede utilizar la llamada msgctl(2)

     #include <sys/msg.h>

     int msgctl(int msqid, int cmd, struct msqid_ds *buf );

msqid es el identificador de la cola, retornado por msgget
cmd es un comando, en este caso IPC_RMID
El tercer parmetor se pone a nulo cuando se pone IPC_RMID.

El siguiente ejemplo muestra como remover una cola de mensajes.

#include <sys/msg.h>


main() { 


	int qid;

	/*invocar la llamada mssget, para un cola con llave 123456
	y se crea la cola con permisos de lectura y escritura para
	propietario, grupo y otros usuarios*/
	qid= msgget(123456,0);

	if (qid < 0) {
		perror("Error al crear la cola de mensajes");
		exit(1);
	}

	/* destruir la cola con la llamada msgctl, el segundo parametro,
	con valor IPC_RMID indica esta instruccion
	*/
	if ( msgctl(qid,IPC_RMID,0) < 0) {
		perror("Error al remover la cola de mensajes");
		exit(2);
	}


	printf("Cola destruida\n");

	exit(0);


}

Con el comando

ipcrm -q IdCola

se puede remover tambien la cola.

Para poder enviar un mensaje en la cola, se utiliza la llamada msgsnd(2).

     #include <sys/msg.h>

     int msgsnd(int msqid, const void *msgp, size_t msgsz,
          int msgflg);

msqid es el identificador de la cola, proporcionado por una llamada msgget.
msgp es un apuntador a void y debe apuntar a la estructura de datos que
contiene los datos del mensaje o el mensaje como tal.
msgsz indica el tamao del mensaje.
msggflg son banderas.

La estructura de datos a la que debe apuntar el segundo parmetro debe
ser del siguiente tipo:

struct mensaje {
	long mtype; /*tipo de mensaje*/
	char mtext[1]; /*campo de cualquier tipo, aqui se especifica un
			solo caracter*/
}


Para poder recibir un mensaje, se debe utilizar la llamada mssrcv(2):

     #include <sys/msg.h>

     int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
          int msgflg);

msqid es el identificador de la cola, proporcionado por una llamada msgget.
msgp es un apuntador a void y debe apuntar a la estructura de datos que
contiene los datos del mensaje o el mensaje como tal.
msgsz indica el tamao del mensaje a recibir.
msgtyp es el tipo de mensaje a recibir, si es 0, se recibe cualquier mensaje,
si es positivo se recibe un mensaje con ese tipo.
msgflg son banderas, si se desea bloquear al proceso hasta que reciba un
mensaje de tipo msgflg, se debe poner a 0; de lo contrario con la bandera
IPC_NOWAIT.

Esta llamada retorna el nmero de bytes colocados en la estructura apuntada por
msgp o un valor negativo ante un error.

El siguiente ejemplo muestra un proceso que crea una cola de mensajes
y se queda suspendido hasta que reciba un mensaje de tipo 1.


#include <sys/msg.h>

struct mensaje {
	long mtype;
	char buffer[20]; 
}

main() { 


	int qid;
	struct mensaje mensaje;
	int nbytes;

	/*invocar la llamada mssget, para un cola con llave 123456
	y se crea la cola con permisos de lectura y escritura para
	propietario, grupo y otros usuarios*/
	qid= msgget(123456,IPC_CREAT|0666);

	if (qid < 0) {
		perror("Error al crear la cola de mensajes");
		exit(1);
	}

	printf("Cola creada\n");

	printf("Esperando recibir mensaje ...\n");

	/*espera recibir un mensaje de tipo 1 y con longiut de 25 bytes*/
	nbytes=msgrcv(qid, &mensaje,25,1,0);

	if (nbytes < 0) {
		perror("Error al recibir el mensaje");
		exit(2);
	}
	printf("mensaje de %d bytes con valor %s\n",nbytes,mensaje.buffer);
	exit(0);


}
Ejemplo19
Cola creada
Esperando recibir mensaje ...


Este proceso se queda bloqueado hasta que otro proceso ponga un mensaje en
la cola de mensajes. El siguiente programa ilustra como efectuar esta
operacion:

#include <sys/msg.h>

struct mensaje {
	long mtype;
	char buffer[20]; 
}

main() { 


	int qid;
	struct mensaje mensaje={1,"Hola mundo\n"};

	/*invocar la llamada mssget, para un cola con llave 123456
	y se crea la cola con permisos de lectura y escritura para
	propietario, grupo y otros usuarios*/
	qid= msgget(123456,0);

	if (qid < 0) {
		perror("Error al conectarse a la cola de mensajes");
		exit(1);
	}


	printf("Enviando  mensaje ...\n");
	/*enviar mensaje sin esperar que el destinatario lo reciba*/
	if (msgsnd(qid, &mensaje,strlen(mensaje.buffer),0) < 0) {
		perror("Mensaje enviado\n");
		exit(2);
	}

	exit(0);


}

Al ejecutar este programa
Ejemplo20
Enviando  mensaje ...

el Ejemplo19 que se quedeo en estado de espera, contesta:

mensaje de 11 bytes con valor Hola mundo


Si el Ejemplo20 se arranca, sin estar activo el Ejemplo20, este puede depositar
la informacin.

Este esquema de comunicacin permite implantar procesamiento de tipo
cliente/servidor. 

Por ejemplo, se va a construir un servidor, en el cual, si recibe un
tipo de mensaje 1, indica que se debe poner a maysculas el mensaje,
si es de tipo 2, debe ponerlo a minsculas. 
El proceso cliente se queda bloqueado hasta que recibe un mensaje de tipo
3, que indica la respuesta del servidor.
#include <sys/msg.h>

struct mensaje {
	long mtype;
	char buffer[25]; 
}

main() { 


	int qid;
	struct mensaje mensaje;
	int i,nbytes;

	/*invocar la llamada mssget, para un cola con llave 123456
	y se crea la cola con permisos de lectura y escritura para
	propietario, grupo y otros usuarios*/
	qid= msgget(123456,IPC_CREAT|0666);

	if (qid < 0) {
		perror("Error al crear la cola de mensajes");
		exit(1);
	}

	printf("Cola creada\n");

	
	while (1) {
		printf("Esperando recibir mensaje ...\n");

		/*espera recibir un mensaje de  tipo (1 o 2) y 
		con longiut de 25 bytes*/
		nbytes=msgrcv(qid, &mensaje,25,-2,0);

		if (nbytes < 0) {
			perror("Error al recibir el mensaje");
			exit(2);
		}

		mensaje.buffer[nbytes]=0;
		printf("mensaje de %d bytes de tipo %d con valor %s\n",
			nbytes,mensaje.mtype ,mensaje.buffer);


		if (mensaje.mtype == 1) { /*cambiar a mayusculas*/
			for (i=0;i< strlen(mensaje.buffer);i++) {
				mensaje.buffer[i]=toupper(mensaje.buffer[i]);
			}

		} else if (mensaje.mtype==2) { /*cambiar a minusculas*/
			for (i=0;i< strlen(mensaje.buffer);i++) {
				mensaje.buffer[i]=tolower(mensaje.buffer[i]);
			}
		} 

		/*si no coincide con ningun tipo, envia el mensaje en
		su formato original*/

		/*enviar mensaje de respuesta*/
		mensaje.mtype=3;
		mensaje.buffer[nbytes]=0;
		if (msgsnd(qid,&mensaje,strlen(mensaje.buffer),0) < 0 ) {
			perror("Error al enviar mensaje de respuesta");
			exit(3);
		}

	}    
	exit(0);


}



#include <sys/msg.h>

struct mensaje {
	long mtype;
	char buffer[25]; 
}

main() { 


	int qid;
	struct mensaje mensaje1={1,"Hola mundo\n"};
	struct mensaje mensaje2={2,"aDIOS mUNDO\n"};
	struct mensaje respuesta;
	int nbytes;

	/*invocar la llamada mssget, para un cola con llave 123456
	y se crea la cola con permisos de lectura y escritura para
	propietario, grupo y otros usuarios*/
	qid= msgget(123456,0);

	if (qid < 0) {
		perror("Error al conectarse a la cola de mensajes");
		exit(1);
	}


	printf("Enviando  mensaje1 ...\n");
	/*enviar mensaje sin esperar que el destinatario lo reciba*/
	if (msgsnd(qid, &mensaje1,strlen(mensaje1.buffer),0) < 0) {
		perror("Mensaje enviado\n");
		exit(2);
	}

	nbytes=msgrcv(qid, &respuesta,25,3,0);
	respuesta.buffer[nbytes]=0;

	if (nbytes < 0) {
		perror("error al leer respuesta");
		exit(3);
	}

	printf("Respuesta de %d bytes es %s\n",nbytes,respuesta.buffer);

	printf("Enviando  mensaje2 ...\n");
	/*enviar mensaje sin esperar que el destinatario lo reciba*/
	if (msgsnd(qid, &mensaje2,strlen(mensaje2.buffer),0) < 0) {
		perror("Mensaje enviado\n");
		exit(2);
	}


	nbytes=msgrcv(qid, &respuesta,25,3,0);
	respuesta.buffer[nbytes]=0;

	if (nbytes < 0) {

		perror("error al leer respuesta");
		exit(3);
	}

	printf("Respuesta de %d bytes es %s\n",nbytes,respuesta.buffer);
	exit(0);


}


Ejercicios.

1. Tomar el Ejemplo21, y prepararlo para que en caso de que reciba una
seal SIGTERM, destruya la cola de mensajes y termine su procesamiento.
2. Construir un juego de gato utilizando mensajes, de tal forma que dos
usuarios puedan interactuar, y realizar sus jugadas de una forma sincronizada.
Utilizar mensajes para este fin.
La entrada del usuario se tomar de la entrada estndar y la salida se
enviar a la salida estndar, no es necesario manipular el ambiente grfico.



Ejercicios.

1. Investigar como se implanta un esquema de semforos, mensajes y memoria
compartida en Windows NT.


+ Monitor de Transacciones.

Un monitor de transacciones es un mdulo de software con las siguientes 
funciones:

+ Administrar procesos. Esto consiste en activar procesos del servidor.
canalizar trabajo hacia dichos procesos, vigilar su ejecucin y
equilibrar su carga de trabajo. Dichos procesos, al inicializarse, crean
conexiones con los recursos que intervienen en una transaccin, con el fin
de hacer eficiente el manejo de la transaccin.

+ Administrar transacciones. Garantiza las propiedades ACID (atmica, 
consistente, aislada y durable) para todos aquellos procesos que son 
administrados por el.

La unidad fundamental de un monitor de transacciones es una transaccin.

Una transaccin es un conjunto de operaciones que se aplican sobre uno
o mas recursos y que afectan el estado de dichos recursos en caso de que
la transaccin es exitosa o no los afecta si la transaccin no se completo.

La arquitectura de un monitor de transacciones es la siguiente:

			  ---------
Peticines ----------	  Servicio1  --- Recursos	
<-->	   Monitor Trans   ---------
Respuestas -----------	  Servicio2  ----Recursos	
			  ---------
			   ....
			  ---------
			  ServicioN  --- Recursos
			  ---------

El monitor de transacciones recibe todas las peticiones de los clientes.
Ante cada peticin, el monitor checa en la tabla de aquellos servicios,
cual de ellos esta libre, y le asigna trabajo (es vlido que dichos servicios
se encuentren distribudos en varias computadoras).

Cada servicio es responsable de llevar a cabo el conjunto de operaciones
sobre los recursos para concretar la transaccin.

Los servicios, cuando el monitor de transacciones se arranca, son inicializados,
como procesos del sistema operativo.
