Gabriel Astudillo Muñoz Gabriel Astudillo Muñoz 1. Administración de Procesos 1.2.2. Métodos para lograr la exclusión mutua. 1.1. Comunicación entre procesos (IPC). 1.2.2.1. Alternancia estricta • Al existir más de un proceso “ejecutándose” (en estado Ready, Running o Blocked), se da el problema de concurrencia de procesos. • Cada proceso pide recursos del sistema computacional, como por ejemplo memoria o un archivo en especial y es deber del kernel permitir la comunicación entre ellos para sincronizar los recursos compartidos. 1.2. Condiciones de competencia. • Dos o más procesos leen o escriben en ciertas zonas compartidas. • El resultado final depende de lo que cada proceso ejecutó y cuándo lo ejecutó ! existe la necesidad de eliminar estas condiciones. • Exclusión Mutua: concepto que garantiza que si un proceso utiliza un recurso compartido, los demás no pueden utilizarlo. • Sección Crítica: Parte del programa en la que tiene acceso al recurso compartido. Proceso 1 While (1) { while(turn != 0) espera(); } seccion_critica(); turn = 1; seccion_nocritica(); } While (1) { while(turn != 1) espera(); } seccion_critica(); turn = 0; seccion_nocritica(); } Se definen los siguientes tiempos de ejecución: o Seccion_critica() : 3 [seg] en ambos procesos. o Seccion_nocritica(): 3[seg] en Proceso 0 y 60[seg] en Proceso 1. • ! Para evitar que existan condiciones de competencia, se debe evitar que dos o más procesos accedan al mismo tiempo a la Sección Crítica. 1.2.1. Solución • Proceso 0 Condiciones necesarias y suficientes para obtener una buena solución: 1. Dos procesos no deben encontrarse al mismo tiempo dentro de sus secciones críticas. 2. No se deben hacer hipótesis sobre la velocidad o el número de CPU. 3. Ningún proceso que esté en ejecución fuera de su sección crítica puede bloquear a otros procesos. 4. Ningún proceso debe esperar eternamente para entrar a su sección crítica. 1 Además, en t=1, turn=0. Tiempo 1 2 3 4 5 6 7 8 9 10 11 12 13 ... turn 0 0 0 1 1 1 0 0 0 1 1 1 1 Estado Proceso 0 seccion_critica() seccion_critica() seccion_critica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_critica() seccion_critica() seccion_critica() seccion_nocritica() seccion_nocritica() seccion_nocritica() espera() Estado Proceso 1 espera() espera() espera() seccion_critica () seccion_critica () seccion_critica () seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() Problema: El proceso 0 está bloqueado a causa del proceso 1, ya que no ha actualizado la variable turn, ya que todavía no sale de su sección crítica. ! esta no es una buena solución (contradice condición 3) . Además, un mismo proceso no puede ingresar a su sección critica dos veces seguidas. Posee espera ocupada. 2 Gabriel Astudillo Muñoz Gabriel Astudillo Muñoz 1.2.2.2. Solución de Peterson. Esta solución fue propuesto por Peterson en 1981. Cada proceso, antes de utilizar las variables compartidas, llama a una función (entra_region) con su propio número de proceso (0 ó 1 como ejemplo). Esta llamada, provoca una espera del proceso hasta que puede entrar a la región crítica. #define FALSE 1==2 #define TRUE 1==1 #define TOTAL_PROC 2 int turno; int interesados[TOTAL_PROC]; void entra_region(int proceso){ int otro_proceso; otro_proceso = 1 – proceso; interesados[proceso] = TRUE; turno = proceso; while(turno == proceso && interesados[otro_proceso] == TRUE){ espera(); //No puedo entrar a la region critica!! } } void sale_region(int proceso){ interesados[proceso] = FALSE; } Esta es una buena solución al problema de competencia, pero se basa en una espera ocupada. Suponiendo que tenemos los mismos procesos que en el ejemplo anterior, se puede realizar la siguiente tabla temporal (Los estados de las variables turno e interesados se dejan como inquietud): 3 Proceso 0 main{ ... entra_region(0); seccion_critica(); sale_region(0) seccion_nocritica(); entra_region(0); seccion_critica(); sale_region(0) } Tiempo 1 (*) 2 3 4 5 6 7 8 9 10 11 ... Proceso 1 main{ ... entra_region(1); seccion_critica(); sale_region(1) seccion_nocritica(); entra_region(1); seccion_critica(); sale_region(1) } Estado Proceso 0 entra_region(0) espera() espera() seccion_critica() seccion_critica() seccion_critica() sale_region(1) seccion_nocritica() seccion_nocritica() seccion_nocritica() entra_region(0) seccion_critica() ... Estado Proceso 1 entra_region(1) seccion_critica() seccion_critica() sale_region(1) seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() seccion_nocritica() ... Nota: En t=1, se produce una condición de competencia, ya que ambos procesos llaman al mismo tiempo a la función entra_region(). Esto se traduce a que ambos procesos almacenan su número en la variable turn, pero sólo cuenta la última operación. Se supone que el proceso que ganó fue el proceso 1. 4 Gabriel Astudillo Muñoz 1.2.2.3. Instrucción TSL (Test and Set Lock) • Lee el contenido de una palabra de memoria en un registro para después almacenar un valor distinto de cero en esa dirección • Las operaciones de lectura y almacenamiento de la palabra tienen la garantía de ser indivisibles • Se emplea una variable compartida, flag. Ésta coordina el acceso al recurso compartido. • Cuando flag==0, cualquier proceso puede darle el valor 1 mediante la instrucción TSL y después leer o escribir en el recurso compartido. Al terminar, el proceso vuelve a hacer flag=0. enter_region: tsl register,flag // copia flag al registro y hace flag=1 cmp register,#0 // ¿flag = 0? jnz enter_region // si era distinto de cero, la cerradura // estaba establecida por lo que hay // que hacer un ciclo ret // retorno a quien hizo la llamada; leave_region: mov flag,#0 ret // almacena un 0 en flag // regresa a quien hizo la llamada Gabriel Astudillo Muñoz 1.2.2.4. Semáforos. • Método desarrollado por Dijkstra. • Un semáforo es una variable entera (positiva), cuyo valor sólo puede ser accesado mediante las operaciones wait y signal además de su inicialización, cuya definición es: (en algunos textos, wait = down y signal=up) WAIT: if (s<=0) s--; SIGNAL: s++; • WAIT: verifica si el valor de un semáforo es mayor que cero y en este caso decrementa dicho valor y el proceso continúa. Si es cero, el proceso se va a dormir. • SIGNAL: incrementa el valor del semáforo respectivo. Si uno o más procesos dormían y no podían completar una operación DOWN anterior, el SO elige alguno de ellos y se le permite terminar la operación WAIT. ! después de un SIGNAL en un semáforo con procesos durmiendo, dicho semáforo seguirá con valor 0, pero habrá un menor número de procesos durmiendo. • Las modificaciones al valor del semáforo sólo se ejecutan en forma indivisible, es decir, si un proceso está modificando un semáforo ningún otro proceso puede esta modificando el mismo valor • Ejemplo: /*Semáforo mutex compartido por N procesos e inicializado en 1*/ /*Estructura del proceso Pi */ while (TRUE) { wait(mutex); /*Sección Critica*/ signal(mutex); /*Sección No Crítica*/ } 5 6 Gabriel Astudillo Muñoz 1.2.3. Bloqueo Mutuo y Aplazamiento Indefinido. • Cuando un proceso de un sistema multiprogramado espera un evento que nunca va a ocurrir, dicho proceso se encuentra en un estado de bloqueo • En un bloqueo de sistema pueden intervenir uno o mas procesos (bloqueo mutuo). • El aplazamiento indefinido (starvation, inanición) está íntimamente relacionado con el bloque mutuo. • Un proceso que no esté bloqueado, puede estar esperando un evento que, por ejemplo, dado la política de asignación de recursos, nunca va a ocurrir. • Un conjunto de procesos está en deadlock cuando cada proceso en el conjunto está esperando por un evento que solo puede ser causado por otro proceso en el conjunto. • Ejemplo: Proceso A Proceso B wait(s); wait(q); /* SC */ signal(q); signal(s); wait(q); wait(s); /* SC */ signal(s); signal(q); ¿Dónde y cuándo se produce el bloqueo mutuo? 7