Unidad de Arquitectura de Ordenadores

Anuncio
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
Unidad de Arquitectura de Ordenadores
y Sistemas Operativos
Shared Memory & Semaphores
Recopilado por: Remo Suppi
Octubre 1997
Publicación exclusiva para fines docentes
Bibliografia: Advanced Unix Programming-Rockhind, Solaris 2.5 User´s manuals o comando man
Semáforos en Unix System V
Estos apuntes son una breve introducción a la utilización de semáforos en sistema operativo Unix SV.
Para mayor detalle recurra a la bibliografía recomendada y a los manuales de usuario/programador.
Introducción:
La utilización de mensajes para sincronizar procesos, si bien es mucho más eficiente que utilizar
archivos, introducen una carga considerable en el sistema ya que el contenido del mensaje se 'mueve'
entre los procesos. Una alternativa más eficiente sería incrementar/decrementar en forma controlada una
variable. Unix SV posee un conjunto de llamadas específicas para trabajar con estas variables
denominadas semáforos. Estas llamadas son más elaboradas que las utilizadas P & V (Wait & Signal Down & Up) utilizadas en la bibliografía de Sistemas Operativos (por ejemplo W. Stalling).
Estas llamadas son complejas para describirlas completamente debido a sus múltiples prosibilidades
( "To be honest, I must admit that I don´t understand them completely" Rockhind). Aqui se describirá su
utilización y la implementación de las primitivas P & V, pero para mayor referencia utilizar el
Programmer User´s Manual - Solaris 2.5 (o comando man).
Existen tres llamadas para trabajar con semáforos:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key, nsems, flags) /*obtiene el sid (identificador del
semáforo)*/
key_t key; /*Llave del semáforo*/
int sems; /*Cantidad de semáforos*/
int flags; /*Opciones*/
Retorna el sid (semaphore set identificator) del semáforo o -1 sobre un
error
int semop(sid, ops, nops) /*opera sobre un semáforo*/
int sid; /*ID del semáforo*/
struct sembuf (*ops)[]; /*Ptr al array de operaciones*/
size_t nops; /*Nº de operaciones*/
Retorna el valor del semáforo previo a la última operación o -1 sobre
un error
int semctl(sid, snum, cmd, arg) /*Control sobre un semáforo*/
int sid; /*ID del semáforo*/
int snum; /*Nº del semáforo*/
int cmd; /*Comando*/
union *arg; /*Argumentos*/
Retorna el valor dependiendo del comando o -1 sobre un error
Semget obtiene un array de semáforos, y ellos son manjados todos a la vez -atómicamente- por un
array de operaciones dado en Semop. Semget traslada una LLAVE (key) sobre un ID que representa un
conjunto de semáforos. Si el bit de IPC_CREAT que existe en flags está activo, el set de semáforos es
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
creado si no existe. Existen nsems semáforos en el conjunto y la numeración de los semáforos comineza
en 0. NO existe ningún problema que nsems=1 por lo cual el conjunto de semáforos tendrá solo un
elemento.
Semop es utilizado para operar sobre el semáforo (adquire & release). Ops es una estructura con tres
enteros:
struct sembuf
short sem_num; /*Nº del semáforo*/
short sem_op; /*Operación*/
short sem_flg; /*Opciones de operación*/
La operación puede ser positiva o negativa o cero. A diferencia de la primitiva P, la cual se espera
hasta que el semáforo pueda ser decrementado en 1, esta permite esperar hasta que el semáforo pueda ser
decrementado por una cantidad arbitraria. Esta característica incluye los valores -1 y 1 las cuales
corresponderían a P y V. If op=-1 el proceso se bloqueará hasta que el semáforo pueda ser decrementado
en 1 sin que el valor sea negativo. Si op=1 el semáforo es incrementado en 1. Para ambas operaciones,
flags puede ser igual a SEM_UNDO, el cual permite que P y V sean balanceadas
automáticamentecuando el proceso realiza un exit.
Semctl puede ser utilizado para interrogar o cambiar el dueño de un semáforo, permisos, etc. También
permite obtener cuantos procesos están esperando sobre un semáforo y el identificador del proceso (pid)
del último que cambio su valor.
P & V implementadas con las llamadas Semget & Semop
Para implementar estas primitivas, se ha utilizado una función -semcreat- que traslada la LLAVE al
SID (indentificador del conjunto de semáforos). En general, semcreat será llamada una vez al cominezo
de cada proceso que utiliza el semáforo.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semcreat(key) /*Traslada key en SID*/
int key;
{
int sid;
if ((sid = semget ((key_t)key, 1, 0666|IPC_CREAT)) == -1)
perror("SemCreat: ");
return (sid);
}
static void semcall(sid, op) /*Opera sobre el semáforo*/
int key;
int op;
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = op;
sb.sem_flg = 0;
if (semop (sid, &sb, 1) == -1) perror("SemCall: ");
}
void P(sid) /*Adquiere el semáforo*/
int sid;
{
semcall (sid, -1);
}
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
void V(sid) /*Libera el semáforo*/
int sid;
{
semcall (sid, 1);
}
Esta implementación de P & V es mucho más rápida en tiempo de ejecución que utilizando mensajes
o FIFOS y por supuesto que utilizando archivos.
Memoria compartida (Shared Memory) - Introducción
La forma más rápida de que un proceso tenga la misma información que otro es no mover los datos
entre procesos sino compartirlos. El que envía (Sender) y el que recibe (Receiver) comparten alguna
zona de memoria principal, y cuando el dato es colocado aquí por el S, el dato es instantaneamente
disponible para ser utilizado por el R. Un semáforo o mensaje deberá ser utilizado para preveer que el R
no lea los datos antes que los mismos estén disponibles y prevenir que el S escriba nuevos datos antes
que los anteriores hayan sido leídos por el R. Unix SV soporta shared memory entre un conjunto
(cualquiera) de procesos. La memoria compartida es llamada segmento. Cada proceso puede tener
diferentes zona de memoria compartida entre diferentes subconjuntos de procesos y cada proceso podrá
acceder a las zonas de memoria que comparte. El segmento de memoria es creado al inicio fuera del
espacio de direcciones de cada proceso, por ello cada proceso que desee acceder a esta zona compartida
ejecutará una llamada al sistema para "mapear" esta zona dentro de su espacio de direcciones. Una vez
realizada esta operación, acceder a la zona compartida es equivalente a acceder a una variable local del
proceso.
Memoria compartida (Shared Memory) en Unix SV
Las llamadas de al Shared Memory (SHM) tienen algún parecido a las utilizadas en semáforos.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key, nbytes, flags) /*obtiene el segment-ID de la
SHM*/
key_t key; /*Llave*/
int nbytes; /*tamaño del segmento*/
int flags; /*Opciones*/
Retorna el segment-ID o -1 sobre un error
int *shmat(segid, addr, flags) /*"Mapea" el segment en la zona de
memoria del usuario*/
int segid; /*ID del segment*/
char *addr;/*Posición deseada*/
int flags; /*Opciones*/
Retorna la dirección del segmento o -1 sobre un error
int shmdt(addr) /*"Libera" el segment de la zona de memoria del
usuario*/
char *addr;/*Dirección del segmento*/
Retorna 0 si bien o -1 sobre un error
int shmctl(segid, cmd, sbuf) /*Control sobre un segment*/
int sid; /*ID del segment*/
int cmd; /*Comando*/
struct shmid_ds *sbuf; /*Puntero al status buffer*/
Retorna 0 si bien o -1 sobre un error
Shmget traslada la LLAVE en un identificador de segmento. Si el flags es IPC_CREATE, el
segmento es creado. Los 9 bits menos significativos de flags son los permisos; permiso de lectura
significa sólo lectura, mientras que escritura significa lectura y escritura.
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
Shmat "mapea" (attach) un segmento de SHM creado con shmget dentro del espacio de direcciones del
usuario. El usuario de este segmento puede solicitar un lugar en particular indicando la dirección por
medio de addr. Si este valor es igual a 0, el kernel del operativo seleccionará un dirección
(recomendable). Shmat retornará un puntero al número de bytes solicitados. A partir de aquí se puede
leer-escribir datos utilizando los operadores normales de C. Otro proceso que necesite compartir estos
datos, "mapeará" este segmento (identificado por key) en su espacio de direcciones. A partir de este
momento los procesos puede comaprtir datos libremente.
Shmdt es ultizado para liberar un segmento compartido cuando se lo no necesite más. El segmento de
memoria no desaparece, sino que desde el proceso que realiza el shmdt no es visible, pudiendo mapearlo
nuevamente. El segmento puede ser destruido utilizando Shmctl con el comando IPC_RMID. Debe
tenerse cuidado con esta llamada ya que es un error serio destruir un segmento que está "mapeado" en el
espacio de direcciones de otro proceso.
Detalles sobre las Llamadas:
Los flags que utiliza shmat() están dados por una unión de diferentes elementos (generalmente se
hace con un and o un or ). En el manual cada uno de los operandos del or se denominan { token}. Los
token que pueden ser utilizados como permisos para la zona de memoria asociada pueden ser
interpretados como los siguientes:
00400 READ by user
00200 WRITE by user
00040 READ by group
00020 WRITE by group
00004 READ by others
00002 WRITE by others
Cualquier combinación válida de estos valores es posible por ejemplo 00666. Consultar Shared Memory
Operation Permissions de intro en la sección 2 del manual para más información (man -s 2 intro).
El SHM segment es "mapeado" al segmentos de datos de un proceso cuando el proceso lo solicita a
través de shmat() (Cuando (flags&SHM_SHARE_MMU) es verdadero el segmento en cuestión es
shared y además puede ser compartido por los restantes procesos que los soliciten) y la dirección
especificada sigue algunos de los criterios siguientes:
Si shmadd es igual a ( void *) 0, el segmento es "mapeado" a la
primera dirección disponible seleecionada por el SOp.
Si shmadd es igual a (void *) 0 y (flags&SHM_SHARE_MMU) es
verdadero, el segmento es "mapeado a la primera dirección "aliñada"
(first available aligned address). Ver las NOTAS en el manual para más
detalles.
Si shmadd NO es igual a ( void *) 0 y (flags&SHM_RND) es
verdadero el segmento es "mapeado en la dirección dada por (
shmaddr - ( shmaddr modulus SHMLBA)).
Si shmadd NO es igual a ( void *) 0 y (flags&SHM_RND) es falso, el
segmento es "mapeado en la dirección dada por shmaddr.
El segmento es asociado para lectura si (flags&SHM_RONLY) es
verdadero, caso contario es asociado como lectura escritura.
Cuando (flags&SHM_SHARE_MMU) es verdadero, los permisos
dados por semget() determinan si el segmento asociado es de lectura o
lectura/escritura.
La llamada shmat retorna la dirección del segmento asociado cuando no existen errores o -1 cuando
existen, la variable errno indica el tipo del error (utilizar perror() para visualizar el error) que pueden ser
de los siguiente tipos (ver el manual para su significado):
EACCES
EINVAL
EMFILE
ENOMEM
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
Los flags que utiliza shmctl() provee una variedad de control sobre las operaciones efectuadas sobre
la SHM y es especificado por cmd. Los valores para los permisos de lectura/escritura son equivalente a
los antes mencionados en shmat().
Existen un conjunto de operaciones específicas ue deben ser indicadas por los siguientes tokens:
IPC_STAT: Ubica el valor actual de cada mienbro de la estructura de
datos asociada con segid dentro de la estructura apuntada por sbuf.
IPC_SET: Inicializa el valor de los mienbros de la estructura de datos
asociada a segid al acoorespondiente valor en la estructura apuntada
por sbuf: shmperm.uid, shmperm.gid, shmperm.mode /*solo accede a
los bits de permisos*/. Este comando sólo puede ser ejecutado por el
proceso que tiene el "effective user ID" igual al del super-usuario o al
valor del shm_perm.cuid, o shm_perm.uid en la estructura de datos
asociada con segid.
IPC_RMID: Borra el SHM segment especidicado por segid del
sistema y destruye las estructura de datos asociada a él. Este comando
sólo puede ser ejecutado por el proceso que tiene el "effective user ID"
igual al del super-usuario o al valor del shm_perm.cuid, o
shm_perm.uid en la estructura de datos asociada con segid.
SHM_LOCK: Bloquea la SHM segment especificada por segid. Este
comando sólo puede ser ejecutado por el proceso que tiene el
"effective user ID" igual al del super-usuario.
SHM_UNLOCK: Desbloquea el SHM segmend especificada por
segid. Este comando sólo puede ser ejecutado por el proceso que tiene
el "effective user ID" igual al del super-usuario.
Los errores que retorna esta función pueden ser del tipo:
EACCES
EFAULT
EINVAL
ENOMEM
EOVERFLOW
EPERM
Para shmget() un identificador de segmento y al estructura de datos asociada es creada para los bytes
solicitados si uno de los siguientes valores es verdadero:
key es igual a IPC_PRIVATE.
key no tiene asociado un SHM y (flags&IPC_CREAT) es verdadero.
La estructura de datos asociada con el nuevo segmento (cuando es creado) es inicializado con:
shm_perm.cuid, shm_perm.uid, shm_perm.cgid y shm_permgid con el
ID del usuario y grupo efectivo respepectivamnete del proceso que lo
solicita.
Los bits de permiso de shm_perm.mode con los bits indicados en flags
(menos significativos).
shm_segsz con el valor del tamaño del segmento.
shm_lpid, shm_nattch, shm_atime y shm_dtime igual a 0.
shm_ctime igual al valor del time actual.
Como retorno da un entero no negativo que es el identificador del segmento. Caso contrario retorna un -1
y errno es inicializado con el tipo de errror.
Los errores que retorna esta función pueden ser del tipo:
EACCES
EEXIST
EINVAL
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
ENOENT
ENOMEM
ENOSPC
semctl() provee una variedad de operaciones de control indicadas por cmd.
Los permisos son idicados por tokens análogos a los utilizados en shmctl(). Las operaciones sobre un
semáforo identificado por sid, snum son las siguientes:
GETVAL: retorna el valor del semáforo semval.
SETVAL: inicializa el valor de semval al valor indicado en arg.val.
(Cuando este comando es realizado, el valor semadj correspondiente a
este semáforo en todos los procesos es borrado). Para ver el
significado de estos valores consultar man -s 2 intro en el apartado de
semáforos.
GETPID: retorna el valor de sempid.
GETCNT: retorna el valor de semncnt.
GETZCNT: retorna el valor de semzcnt.
GETALL: Lee los valores de los semáforos (semvals) en el array
apuntado por arg.array
SETALL: Inicializa los valores de los semáforos con el array apuntado
por arg.array. Esta operación borra la variable semadj correspondiente
al semáforo en todo los procesos.
IPC_STAT: Inicializa el valor de cada mienbro de la estructura de
datos asociada com sid en la estructura apuntada por arg.buf. El
contenido de esta estructura está definido en man -s 2 intro.
IPC_SET: Inicializa el valor de los datos del semáforo (asociado con
sid) a partir de los valores pasados en la estructura arg.buf
(sem_perm.uid, sem_perm.gid, sem_perm.mode) (solo puede ser
ejecutado por el superusuario, o por el proceso que figura en
sem_perm.cuid, sem_perm.uid asociado con sid -quien lo creó-).
IPC_RMID: Borra el semáforo especificado por sid del sistema y
destruye las estructuras de datos asociadas. Solo puede ser ejecutado
por el superusuario, o por el proceso que figura en sem_perm.cuid,
sem_perm.uid asociado con sid -quien lo creó- .
Valores de retorno: Los siguientes cmd retornan como valor:
GETVAL: el valor del semáforo.
GETPID: el valor de sempid (int).
GETNCNT: el valor de semncnt.
GETZCNT: el valor de semzcnt.
Todos los otros: 0 cuando todo está bien, -1 cuando existe error.
Los errores que retorna esta función pueden ser del tipo:
EACCES
EFAULT
EINVAL
EPERM
EOVERFLOW
ERANGE
semget() retorna el id del semáforo asociado con la llave. El semáforo será creado si:
key es igual IPC_PRIVATE
key aún no tiene un semáforo asociado con esta y
(flags&IPC_CREAT) es verdadero.
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
Cuando se crea un semáforo, sem_perm.cuid, sem_perm.uid, sem_perm.cgid, sem_perm.gid son
inicializados con el ID del usuario efectivo y del grupo que está ejecutando el proceso. sem_perm.mode
es inicializado con los bits menos significativos de flags, sem_nsems es inicializado a nsems, sem_otime
igual a 0 y sem_ctime igual a la hora actual.
La función retorna 0 si todo bien, -1 si existe errores (el valor del error estará en errno). Los errores
(errno) que retorna esta función pueden ser del tipo:
EACCES
EEXIST
EINVAL
ENOENT
ENOSPC
semop() es utilizada para realizar operaciones (en forma atómica -indivisible-) sobre todos los
semáforos. La llamada int semop(int semid, struct sembuf *sops, size_t nops) utiliza sops que es un
puntero a un array de estructuras que contendrán la operaciones para los semáforos en cuestion (recordar
que el conjunto de semáforo está identificado por semid). Los miembros de cada una de estas estructuras
está definido por:
short sem_num /*número del semáforo*/
short sem_op /*operación a realizar*/
short sem_flg /*flags de operación*/
Se debe tener en cuenta que cada semáforo esta especificado por la pareja semid, sem_num. Por lo cual
con la semop le podemos indicar en sops el array de punteros a esta estructura para modificar a la vez
todo los semáforos de semid. nops es el número de estructuras que existe en el array sops.
sem_op puede ser un valor negativo, positivo o 0, y la función realizará diferentes acciones según sea:
Sem_op es un entero negativo
Si el valor del semáforo (semval) es mayor o igual al valor absoluto de
sem_op, el valor abs de sem_op es restado del valor de sem_val.
También si (sem_flg&SEM_UNDO) es verdadero el valor abs de
sem_op es sumado al valor semadj de los procesos que lo utilizan (ver
exit(2)).
Si sem val es menor que el valor abs de sem_op y
(sem_flg&IPC_NOWAIT) es verdadero, semop() retorna
inmediatamente.
Si sem val es menor que el valor abs de sem_op y
(sem_flg&IPC_NOWAIT) es falso, semop() incrementa el semncnt
(proceso bloqueados en el semáforo) asociado con el semáforo y
suspende la ejecución del proceso hasta que una de las acciones
siguientes ocurra:
Semval cambia a un valor mayor o igual que el valor abs de
sem_op. Cuando esto ocurre semncnt es decrementado y el
valor se sem_op es restado de semval (idem 1er caso).
El proceso está esperando por un semáforo -semid- y este es
borrado del sistema con semctl(). La llamada semop() retornará
con un error y erno será igual a EIDRM.
El proceso que espera recibe una señal para terminar (abortar la
ejecución), por lo cual el valor de semncnt es decrementado
para mantener la coherencia del semáforo.
Sem_op es un entero positivo
El valor del sem_op es sumado al valor semval y si
(sem_flg&SEM_UNDO) es verdadero el valor de semopj es
decrementado a semadj de lo procesos que lo utilizan.
Escuela de Informática de Sabadell
Ramírez
Jorge Durán
Sem_op es cero
Si el valor del semáforo semval es 0 semop() retorna inmediatamente.
Si semval no es igual a cero y (sem_flg&IPC_NOWAIT) es verdadero
semop() retorna inmediatamente.
Si semval no es igual a cero y (sem_flg&IPC_NOWAIT) es falso,
semop incrementa el valor de semzcnt asociado con el semáforo y
bloque el proceso hasta que una de las siguiente situaciones ocurre:
Semval cambia a cero, además el valor de semzcnt es
decrementado.
El semáforo es borrado del sistema por lo cual la operación
retornará con un error EIDRM.
El proceso que espera recibe una señal para terminar (abortar la
ejecución), por lo cual el valor de semzcnt es decrementado
para mantener la coherencia del semáforo.
Los errores que retorna esta función pueden ser del tipo:
EACCES
EAGAIN
EFAULT
EFBIG
EIDRM
EINTR
EINVAL
ENOSPC
ERANGE
Ejemplo de comunicación entre procesos utilizando Shared Memory y semáforos
Consideremos cinco procesos ABCDE que necesitan compartir datos. A crea los procesos-estructuras
de datos y pone los datos en SHM, BCDE leen de SHM y realizan algua modificación de los datos, DE
escriben en la SHM los datos modificados, y finalmente BC leen de la SHM dichos datos. Para ello
utilizaremos dos semáforos: el primero de ellos permitirá a DE escribir en la SHM cuando todos (4
procesos) hayan teminado de leer. El otro semáforo, permitirá que BC posteriormente lean los datos
escritos por DE:
Proceso A -sólo se muestra el código relevante#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define LLAVE ((key_t) 13)
union semun { /*estructura utilizada por semctl para inicializar los
semáforos*/
int val; /*valor inicial del semáforo*/
struct semid_ds *buf;
ushort *array;
} arg;
DefinirEstructuras(int *shmid, int **shmptr, int *semid)
{
*shmid = shmget(LLAVE, 100*sizeof(int), IPC_CREAT
| 0666); /*SHM*/
if (*shmid < 0) { perror("SHMget"); exit(-1)}
*shmptr = (int *) shmat(*shmid,0,0); /*Mapea el
segmento en cuestión*/
if ((*semid = semget (LLAVE, 2, IPC_CREAT | 0666))
Escuela de Informática de Sabadell
Ramírez
== -1){ perror("Semget"); exit(-2) } /*Creo dos
semáforos*/
arg.val = 4;
semctl(*semid, 0, SETVAL, &arg); Inicializo el
semáforo 0 a 4
arg.val = 2;
semctl(*semid, 1, SETVAL, &arg); Inicializo el
semáforo 1 a 2
... /* aqui el proceso introduce los datos en la SHM */
}
CrearProcesos()
{
char vid[2] /*... declaración de variables varias*/
for (i=1; i<5; i++) /*Creo Procesos*/
{
if (fork() == 0 ) {
printf ("Proceso %d creado", i); sprintf(vid, "%d",
i);
execlp("Codigo_de_su_hijo", "Codigo
_de_su_hijo", vid, NULL); Ejecuto el código del
hijo con arg pasado en vid*/
}
for (i=1; i<5; i++) wait(0); /* Me espero que los hijos
terminen*/
}
Liberar(int shmid, int *shmptr, int semid)
{
shmdt ((char *)shmptr);
shmctl (shmid, IPC_RMIID, 0);
semctl (semid, 0, IPC_RMID, 0);
semctl (semid, 0, IPC_RMID, 0);
}
main(int argc, char *argv[]);
{
int shmid, semid; Identificador de la SHM y conjunto de
semáforos*/
int *shmptr; Puntero a la SHM
DefinirEstructuras(&shmid, &shmptr, &semid);
CrearProcesos();
ImprimirResultado(shmptr);
Liberar(shmid, shmptr, semid);
}
Proceso Xi (BCDE) -sólo se muestra el código relevante#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define LLAVE ((key_t) 13)
struct sembuf ops;
main(int arg_n, char *arg[]);
{
int *ptr; /*puntero a la SHM */
int *mem_local; /*Memoria Local*/
int ident, semid;
Jorge Durán
Escuela de Informática de Sabadell
Ramírez
mem_local = (int *)malloc (100*sizeof(int)); /*Obtengo la
memoria local*/
ident = shmget (LLAVE, 100*sizeof(int), 0666); /*Obtengo la
SHM*/
ptr = (int) shmat (ident, 0, 0); /*Mapeo la SHM en el espacio
de usuario - ptr -*/
semid = semget (LLAVE, 2, 0666); /*Obtengo el identificador
del semáforo*/
switch (arg[1][0]){
case ´1´: /* los procesos BC (1,2) hacen los mismo*/
case ´2´: Load();/* leo los datos desde ptr y realizo
algún trabajo - recordar que este será el B(C) y debe
leer, esperar, y leer*/
ops.sem_num = 0;
ops.sem_op = -1; /*decremento el semáforo 0 en
1*/
ops.sem_flg = 0;
semop (semid, &ops, 1);
ops.sem_num = 1;
ops.sem_op = 0; /*Con este valor semop
esperará hasta que el semáforo en cuestión sea
0 -aquí el sem Nº 1-*/
ops.sem_flg = 0;
semop (semid, &ops, 1);
Load();/*Ahora el proceso B(C) puede leer por
segunda vez desde ptr*/
break;
case ´3´: /* los procesos DE (3,4) hacen los mismo*/
case ´4´: Load();/* leo los datos desde ptr y realizo
algún trabajo - recordar que este será el D(E) y debe
leer, esperar y escribir */
ops.sem_num = 0;
ops.sem_op = -1; /*decremento el semáforo 0 en
1*/
ops.sem_flg = 0;
semop (semid, &ops, 1);
ops.sem_num = 0;
ops.sem_op = 0; /*Con este valor semop
esperará hasta que el semáforo en cuestión sea
0 -aquí el sem Nº 0-*/
ops.sem_flg = 0;
semop (semid, &ops, 1);
Store();/*Ahora el proceso D(E) puede guardar
la información en ptr porque cuando sem[0]=0
indicará que todos han terminado de leer*/
ops.sem_num = 1;
ops.sem_op = -1; /*Decremento el sem[1] que
es el que esperará BC para saber que DE han
terminado de escribir*/
ops.sem_flg = 0;
semop (semid, &ops, 1);
break;
}
shmdt ((char *) ptr);
}
Jorge Durán
Descargar