TEMA VII Comunicación entre procesos - Concurrencia + Concurrencia. Cuando dos o más procesos están 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 ejecución. 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 condición se conoce como un condición de competencia (race condition). Este tipo de condiciones son difíciles de detectar y de reproducir. Por tanto, es necesario que siempre que un recurso se comparte por dos o más procesos, se maneje la concurrencia sobre dicho recurso. Para realizar esto, se deben utilizar mecanismos de intercomunicación entre procesos (IPCs) tales como semáforos, 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 bifurcación de procesos, y en conjunto, el padre e hijos, modifican el archivo. #include #include #include #include #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 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 sección de cógido se conoce como sección crítica. + Semáforos. Una técnica para manipular concurrencia es utilizar semáforos. Un semáforo es un número entero con dos operaciones atómicas (es decir, no se puede suspender su ejecución 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 semáforo, el proceso no puede avanzar hasta que el valor del semáforo sea positivo. Si el valor es positivo, decrementa el valor del semáforo. La operación signal es la encargada de incrementar el valor de un semáforo. Un semáforo se utiliza para controlar el acceso a secciones críticas, asegurando que SOLO un proceso ejecute dicha seccion crítica, mientras los otros esperan. En el ejemplo pasado, se puede poner un semáforo en la siguiente área de código. ... /* 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 semáforo binario. Para poder implantar esta técnica, es necesario recurrir a las llamadas del sistema opertivo UNIX. + Llamadas UNIX para manipular semáforos. La manera como UNIX implanta los semáforos es utilizando el concepto de un conjunto de semáforos. El kernel de UNIX es el encargado de crear un conjunto de semáforos. Dicho conjunto puede ser desde un semáforo hasta el número máximo de semáforos permitido por el sistema operativo. Un conjunto de semáforos se puede concebir como un arreglo de semáforos. Cuando un semáforo es creado, el kernel le da un valor inicial de 0. Es válido que un conjunto de semáforos sea utilizado por distintos procesos. Es por esto, que al kernel, se le debe indicar una etiqueta para nombrar al conjunto de semáforos. La llamada al sistema operativo (no POSIX) que permite crear u obtener una referencia a un conjunto de semáforos es semget(2): #include #include #include int semget(key_t key, int nsems, int semflg); Esta llamada crea un conjunto de semáforos. El primer parámetro es la etiqueta con la que el kernel conoce al semáforo y es un numero entero; el segundo parámetro indica el número de semáforos que se incluyen en el conjunto de semáforos y el último parámetro son las banderas para obtener este semáforo, si el semáforo no existe se puede indicar que sea creado y los permisos. Los permisos de un semáforo son de lectura y alteración, funciona de la misma manera que el concepto de permisos en archivos, por ejemplo 0666 indica un permiso de lectura y alteración para el propietario, grupo y los demás usuarios. La bandera IPC_CREAT permite indicar que el conjunto de semáforos se debe crear. Si el tercer parámetro de semget es 0, solo permite que el proceso obtenga un referencia al conjunto de semáforos. Esta llamada regresa un número entero, mayor que cero, si tuvo éxito e indica un número con el cual se podrá referenciar el conjunto de semáforos dentro del proceso. En caso de que exista un error (no se tiene permisos de acceso). El siguiente ejemplo muestra la creación de un conjunto de semáforos, de tamaño 1. #include #include #include #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 semáforo existe, con el comando ipcs -s ipcs -s IPC status from 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 semáforos activos en el sistema operativo. Para remover el semáforo del sistema operativo, se utiliza la llamada semctl(2): #include #include #include int semctl(int semid, int semnum, int cmd, ...); este recibe como parámetros : El identificador del semáforo, dado por la llamada semget El número de semaforo del conjunto sobre el cual se aplica el comando. El comando a aplicar, que en esta caso es IPC_RMID El cuarto parámetro es opcional, dependiendo del comando a aplicar. El siguiente ejemplo muestra un programa que destruye al conjunto de semaforos creado en el Ejemplo2 #include #include #include #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 as of Wed May 5 19:44:19 1999 T ID KEY MODE OWNER GROUP Semaphores: se muestra que no existe un semáforo. 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 #include #include #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 semáforo, pero de una forma atómica, es la llamada semop(2). #include #include #include int semop(int semid, struct sembuf *sops, size_t nsops); Esta llamada recibe como parámetros lo siguiente: El identificador del conjunto de semáforos, La dirección de la primera posición de un arreglo de estructura sembuf. El número de operaciones (o tamaño 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 semáforos se le aplican las operaciones. El segundo campo indica el tipo de operacion a aplicar sobre el elemento del semáforo, cumpliendo las siguientes reglas: Si sem_op es positivo o negativo implica que se debe alterar el valor del semáforo. Si sem_op es cero, indica lectura sobre el semáforo. Las siguientes condiciones se aplican si: + Si el valor es positivo, el semáforo ve incrementado su valor por lo indicado en sem_op. + Si sem_op es cero y el valor del semáforo es cero, retorna de inmediato de la llamada. + Si sem_op es cero y el valor de semáforo NO ES cero, suspende la ejecución del proceso. Esto es, el proceso se queda esperando hasta que el semáforo 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 operación, que afecta el comportamiento de la operación a realizar. El siguiente ejemplo muestra el uso de esta llamada, donde un solo proceso entra de forma única a una seccion de código. La lógica de este programa es: + Se crea un conjunto de semáforos, con un solo miembro. + Se espera que el semáforo obtenga un valor 0 y se incrementa en una unidad. + Se entra a la sección crítica. + Se decrementa el valor del semáforo. #include #include #include #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 #include #include #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 + Semáforo 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 módulo u objeto que define un semáforo 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 #include #include #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 sincronización. El primer problema que se va a resolver es el Ejemplo1, que es un problema de sección crítica. En este problema se usa un semaforo binario, el cual se coloca en la sección critica, que es la lectura, incremento y escritura del contador. #include #include #include #include #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 #include #include int shmget(key_t key, size_t size, int shmflg); Esta recibe tres parámetros: La llave o etiqueta dentro del kernel. El tamaño 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 algún error, o un número positivo que es igual al identificador de memoria compatida. El siguiente ejemplo crea un segmento de archivo de memoria compartida y de tamaño igual al archivo /etc/passwd. #include #include #include #include #include #include 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 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 #include 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 algún 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 #include #include #include #include #include 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 #include #include #include #include #include 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 #include #include #include #include #include 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 #include #include #include #include #include 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 sección crítica 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 solución a este problema, se deben utilizar semaforos. Para el control de la sección crítica, se puede utilizar un semáforo binario. Pero el control del número de elementos en el buffer, será controlador por un semáforo que toma valores entre 0 y N. Para esto, se debe implantar un semáforo que tome valores entre 0 y N. El siguiente código muestra la implantacion de este semáforo: /*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 #include #include #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 semáforo, 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 continuación se muestra el código: #include "Ejemplo11.h" #include #include #include /*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 #include #include #include #include #include #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 #include #include #include #include #include #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 solución 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 semáforos. + 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 están diseñadas para que un proceso se interese por un mensaje de un tipo determinado. Un mensaje porta información, 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 comunicación permite que uno o más procesos reciban información y que en algún momento se sincronice el acceso a cierto recurso enviando mensajes a ciertos procesos. + Llamadas al sistema para manipular colas de mensajes. Al igual que semáforos y memoria compartida, para cola de mensajes existe una llamada para crear o conectarse a la cola de mensajes, denominada msgget(2) #include int msgget(key_t key, int msgflg); El primer parámetro es la llave del IPC, que es un numero entero. El segundo parámetro es un conjunto de banderas, en caso de aplicar una creación 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 número entero, que indica el identificador de la cola. Si es negativo, existe un error en esta llamada. El siguiente ejemplo muestra la creación de una cola de mensajes. #include 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 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 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 parámetor se pone a nulo cuando se pone IPC_RMID. El siguiente ejemplo muestra como remover una cola de mensajes. #include 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 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 tamaño del mensaje. msggflg son banderas. La estructura de datos a la que debe apuntar el segundo parámetro 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 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 tamaño 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 número 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 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 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 información. Este esquema de comunicación 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 mayúsculas el mensaje, si es de tipo 2, debe ponerlo a minúsculas. El proceso cliente se queda bloqueado hasta que recibe un mensaje de tipo 3, que indica la respuesta del servidor. #include 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 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 señal 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 estándar y la salida se enviará a la salida estándar, no es necesario manipular el ambiente gráfico. Ejercicios. 1. Investigar como se implanta un esquema de semáforos, mensajes y memoria compartida en Windows NT. + Monitor de Transacciones. Un monitor de transacciones es un módulo de software con las siguientes funciones: + Administrar procesos. Esto consiste en activar procesos del servidor. canalizar trabajo hacia dichos procesos, vigilar su ejecución y equilibrar su carga de trabajo. Dichos procesos, al inicializarse, crean conexiones con los recursos que intervienen en una transacción, con el fin de hacer eficiente el manejo de la transacción. + Administrar transacciones. Garantiza las propiedades ACID (atómica, consistente, aislada y durable) para todos aquellos procesos que son administrados por el. La unidad fundamental de un monitor de transacciones es una transacción. Una transacción 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 transacción es exitosa o no los afecta si la transacción no se completo. La arquitectura de un monitor de transacciones es la siguiente: --------- Peticiónes ---------- Servicio1 --- Recursos <--> Monitor Trans --------- Respuestas ----------- Servicio2 ----Recursos --------- .... --------- ServicioN --- Recursos --------- El monitor de transacciones recibe todas las peticiones de los clientes. Ante cada petición, el monitor checa en la tabla de aquellos servicios, cual de ellos esta libre, y le asigna trabajo (es válido que dichos servicios se encuentren distribuídos en varias computadoras). Cada servicio es responsable de llevar a cabo el conjunto de operaciones sobre los recursos para concretar la transacción. Los servicios, cuando el monitor de transacciones se arranca, son inicializados, como procesos del sistema operativo.