Universidad de Carabobo Facultad Experimental de Ciencias y Tecnología Departamento de Computación Sistemas Operativos Prep. Carlos I. Buchart I. Nombre:___________________________________ Sincronizacion y Comunicacion entre Procesos Atomizacion de las operaciones: la arquitectura Intel 386, y posterior de la misma familia, puede asegurar la atomicidad de las operaciones ejecutadas, una a una, simplemente añadiendo el prefijo lock a la instruccion deseada. lock add ax, bx Inhabilitacion de Interrupciones: el problema de la seccion critica se genera cuando dos o mas procesos desean acceder concurrentemente al mismo recurso. La solucion de I.I. evita que el proceso abandone el CPU durante la S.C., logrando asi la exclusion mutua. El gran inconveniente que presenta esta opcion es que desactivar y activar las interrupciones es muy costoso en lo que respecta a tiempo de ejecución, por lo que no es una buena opcion para sistemas en tiempo real, por ejemplo. El esquema general de uso seria el siguiente: void Proceso(int id) { // Desactivar las interrupciones // S.C. // Activar las interrupciones nuevamente // S.n.C. } Test-And-Set: esta funcion verifica el estado de una variable cerrojo devolviendo verdadero si el cerrojo estaba abierto (en 0), falso en otro caso. Ademas, al retornar, el cerrojo queda cerrado (bien sea porque ya lo estaba o porque se cerro dentro de la funcion). Su uso es como se muestra a continuacion: int cerrojo=0; void Proceso(int id) { while(test_and_set(cerrojo)); // S.C. cerrojo=0; // S.n.C. } Swap: la instruccion swap, como su nombre indica, intercambia el valor de dos variables. Al igual que con la instruccion anterior, esta otorga el acceso al primero que lo solicite una vez que el recurso haya sido liberado. Del mismo modo, ambas instrucciones swap y t&s adolescen del problema de “inversion de prioridad”. int cerrojo=0; void Proceso(int id) { int clave=1; do { swap(clave,cerrojo); } while(cerrojo); // S.C. swap(clave,cerrojo); // S.n.C. } Semaforos: propuestos por Dijkstra en 1.965 como una solucion al problema de la seccion critica. Consta de dos instrucciones signal y wait, y una variable mutex comun. Esta variable se suele fijar al numero de recursos disponibles, la operacion wait decrementa su valor y verifica si no es negativo (es decir, que al solicitarse habia alguno disponible); si no lo es, continua la ejecucion, en caso contrario se puede utilizar espera activa o se puede bloquear el proceso e introducirlo en una cola para evitar el consumo de CPU. El signal simplemente libera un recurso y en caso de utilizar colas, libera el primer proceso de la misma. int mutex=MAX_RECURSOS; void signal(int *_mutex) { (_mutex*)++; if((*mutex)<=0) // Desencolar un proceso } void wait(int *_mutex) { (_mutex*)--; if((*mutex)<0) // Encolar un proceso } Paso de mensajes: este sistema de comunicacion tiene diversas implementaciones que solo seran mencionadas: directa e indirecta; sin buffer, con buffer, bien sea limitado o “ilimitado”; con y sin bloqueo. Se basa en un par de instrucciones send y receive, las cuales se encargan de enviar y leer, respectivamente, un determinado mensaje, ya sea directamente a un proceso o bien sea a un buzon o buffer. Por ultimo, otro mecanismo de comunicacion sumamente utilizado en linux, es de las tuberias, que no son mas que flujos unidireccionales de datos implementados por el shell. Simplemente se toma la salida de un proceso y se usa como salida del siguiente. La implementacion de esta tecnica es sumamente sencilla: se crea un archivo temporal en memoria y a este se direcciona tanto el apuntador de stdout del primer proceso como el de stdin del segundo. ls -la | grep 4096 Solucion al problema de los filosofos comensales en Linux, utilizando semaforos: #include <stdio.h> #include <sys/ipc.h> #include <sys/sem.h> #define NUM_FILOSOFOS 5 #define PENSANDO #define COMIENDO 0 1 #define LIBRE #define USADO 1 0 typedef struct datos_filosofos { int filosofos[NUM_FILOSOFOS]; int pids[NUM_FILOSOFOS]; int semaforo_id,semaforo_id2; int memoria_id; } datos_filosofos; struct datos_filosofos *p_filosofos; // Reservar semáforos y memoria compartida int crea_filosofos(void) { int i; int sem_id,sem_id2,mem_id; // Crea el área de memoria compartida if((mem_id=shmget(IPC_PRIVATE,sizeof(datos_filosofos),0774 | IPC_CREAT))==-1) return -1; if(!(p_filosofos=(datos_filosofos *)shmat(mem_id,(char *)0,0))) return -2; // Crea el set de semáforos para los cubiertos if((sem_id=semget(IPC_PRIVATE,NUM_FILOSOFOS,0774 | IPC_CREAT))==-1) return -3; // Crea el semáforo que evitará el interbloqueo if((sem_id2=semget(IPC_PRIVATE,1,0774 | IPC_CREAT))==-1) return -3; // Inicializa el área de memoria compartida for(i=0;i<NUM_FILOSOFOS;i++) { p_filosofos->filosofos[i]=PENSANDO; p_filosofos->pids[i]=0; semctl(sem_id,i,SETVAL,LIBRE); } semctl(sem_id2,0,SETVAL,NUM_FILOSOFOS-1); p_filosofos->semaforo_id=sem_id; p_filosofos->semaforo_id2=sem_id2; p_filosofos->memoria_id=mem_id; return sem_id; } // Liberar semáforos y memoria compartida void elimina_filosofos(void) { int i; int mem_id,sem_id; mem_id=p_filosofos->memoria_id; shmdt((char *)p_filosofos); shmctl(mem_id,IPC_RMID,(struct shmid_ds *)NULL); for(i=0;i<NUM_FILOSOFOS;i++) semctl(sem_id,i,IPC_RMID); } // Wait void P(int id,int i) { struct sembuf op[3]={i,-1,0}; semop(id,op,1); } // Signal void V(int id,int i) { struct sembuf op[3]={i,1,0}; semop(id,op,1); } int main() { int res; int i; // Crea filósofos: reservar memoria, crear semáforos res=crea_filosofos(); if(res<0) { printf("Error %d\n",-res); return 0; } // Crea los filósofos (procesos hijos) for(i=0;i<NUM_FILOSOFOS;i++) if(!fork()) { // Proceso hijo #i p_filosofos->pids[i]=getpid(); while(1) { // Máximo NUM_FILOSOFOS-1 entran a tratar de comer para evitar el interbloqueo P(p_filosofos->semaforo_id2,0); // Espera los dos cubiertos P(p_filosofos->semaforo_id,i); P(p_filosofos->semaforo_id,(i+1)%NUM_FILOSOFOS); p_filosofos->filosofos[i]=COMIENDO; sleep(1); // Devuelve los dos cubiertos V(p_filosofos->semaforo_id,i); V(p_filosofos->semaforo_id,(i+1)%NUM_FILOSOFOS); p_filosofos->filosofos[i]=PENSANDO; // Deja que otro trate de comer V(p_filosofos->semaforo_id2,0); } } // Bucle de espera del proceso padre while(res!=27) { // Muestra información printf("Estado de los filósofos: \n"); for(i=0;i<NUM_FILOSOFOS;i++) printf("%d[%d] ==> %d\n",i,p_filosofos->pids[i],p_filosofos->filosofo[i]); res=getchar(); } // Elimina los procesos hijos for(i=0;i<NUM_FILOSOFOS;i++) kill(p_filosofos->pids[i],9); elimina_filosofos(); return 0; } Otros problemas a resolver: productor-consumidor, lectores-escritores. Bibliografía STALLINGS, William – Sistemas Operativos, segunda edicion. Pag. 175-209 TANENBAUM, Andrew – Sistemas Operativos, Diseño e Implementacion. Pag. 59-82 SILBERCHATZS, a – Sistemas Operativos, sexta edicion. Pag. 181-204 http://bernia.disca.upv.es/lxr/http/source/ http://es.tldp.org/Manuales-LuCAS/DENTRO-NUCLEO-LINUX/dentro-nucleo-linux-html/dentro-nucleolinux-2.html http://www.icselectionguide.com/real_time_dispatch.html http://www.codecomments.com/archive258-2004-12-343766.html http://www.linux-tutorial.info/modules.php?name=Tutorial&pageid=288 CB/cb