Programación Concurrente 1 Exclusión mutua Exclusión mutua Definición del problema: Dos procesos comparten un recurso. El acceso a dicho recurso debe ser exclusivo. Existen dos soluciones alternancia y el Algoritmo de Dekker. Alternancia Consigue la exclusión mutua. Los accesos al recurso son de manera estrictamente alternada. PROGRAM Alternancia VAR turno: [1..2] PROCEDURE P1; BEGIN REPEAT WHILE turno = 2 DO; (*espera*) Uso del recurso Turno := 1; Otras acciones FOREVER END; PROCEDURE P2; BEGIN REPEAT WHILE turno = 1 DO; (*espera*) Uso del recurso Turno := 2; Otras acciones FOREVER END; COBEGIN P1; P2; COEND END; La solución por alternancia tienen el defecto de que los accesos al recurso son estrictos en su alternancia de manera que si un proceso necesita más el recurso deberá esperar hasta que el otro lo use, esto es debido a la variable turno, que es la que sincroniza ambos procesos. La solución obvia parece ser utilizar dos variables que indiquen si el proceso está usando el recurso o no, de forma que antes de usarlo, el proceso compruebe si está siendo utilizado, y en caso de poder usar el recurso indica que lo usa él. Esta solución conduce a una falta de exclusión mutua ya que ambos procesos pueden comprobar que el otro no usa el recurso e intentar acceder simultáneamente al mismo. Esto se produce por que realiza primero la comprobación del uso del recurso por parte del otro recurso y luego se modifica su variable. Esto se podría intentar solucionar indicando primero que se quiere acceder al recurso y comprobar luego que el mismo está libre, lo que nos lleva a una espera infinita, que se puede solucionar mediante un tratamiento de cortesía, cuando un proceso ve que el otro quiere usar el recurso le cede turno. Esto nos puede conducir a que los procesos se queden de manera indefinida cediéndose el paso. La solución a la exclusión mutua la proporciona el algoritmo de Dekker. Se utilizan tres variables, las dos que indican el deseo de uso del recurso más una tercera que indica a quien se le concede el recurso en caso de conflicto. Este algoritmo generalizado para N procesos sólo tiene interés teórico ya que consume mucho tiempo para conseguir la exclusión mutua. Página 1 Programación Concurrente PROGRAM Algortimo_de_Dekker VAR Recurso_1, Recurso_2 (Usado, No_Usado); Turno [1..2]; PROCEDURE P1; BEGIN REPEAT Recurso_1 := Usado; WHILE Recurso_2= Usado DO; IF Turno=2 THEN BEGIN (*tratamiento de cortesia*) Recurso_1:= No_Usado; WHILE turno = 2 DO (*espera*) Recurso_1:= Usado; END; Uso del recurso Turno := 2; Recurso_1:= No_Usado; Otras acciones FOREVER END; PROCEDURE P2; BEGIN REPEAT Recurso_2 := Usado; WHILE Recurso_1= Usado DO; IF Turno=2 THEN BEGIN (*tratamiento de cortesia*) Recurso_2:= No_Usado; WHILE turno = 2 DO (*espera*) Recurso_2:= Usado; END; Uso del recurso Turno := 1 Recurso_2:= No_Usado; Otras acciones FOREVER END; BEGIN Recurso_1:= No_Usado; Recurso_2:= No_Usado; Turno:=1 COBEGIN P1;P2; COEND; END; Página 2 Programación Concurrente 2 Herramientas para manejar la concurrencia Región crítica Varios procesos concurrentes pueden compartir una misma variable, por consistencia se debe evitar que mientras un proceso accede a una variable el otro la modifique, es decir, se debe conseguir la exclusión mutua de los procesos respecto a la variable compartida. Los criterios de corrección de la exclusión son los siguientes: 1. Exclusión mutua con respecto al recurso 2. Cuando existen N procesos con acceso al recurso, se le concede acceso a uno en un tiempo finito. 3. Se libera el recurso en un tiempo finito. 4. No existe espera activa. El algortimo de Dekker conlleva espera activa de los procesos lo que provoca perdidas de eficiencia en el procesador. Para conseguir la exclusión mutua se usa la región crítica (RC). La semántica de la RC establece que: • Los procesos concurrentes sólo pueden acceder a las variables compartidas dentro de sus correspondientes RC. • Un proceso que quiera entrar a una RC lo hará en tiempo finito. • En un instante t sólo un proceso puede estar dentro de la RC de una variable. • Un proceso abandonará la RC en tiempo finito. Con estas cuatro características se puede asegurar que: 1. Si el número de procesos dentro de una RC es 0 el proceso que lo desee puede entrar. 2. Si el número de procesos dentro de una RC es 1 el proceso que lo desee debe esperar. 3. Cuando un proceso sale de RC se permite que entre uno de los que esperan. 4. Las decisiones de quien entra y cuando abandona una RC se tomará en tiempo finito. 5. Se supone que la cola de espera es justa y pasiva. En PASCAL una RC se define: ... VAR v: SHARED tipo; ... PROCEDURE P1; BEGIN ... REGION v DO acción con v ... END ... Semáforos Si tenemos varios procesos que deban trabajar de forma sincronizada se precisa un nuevo constructor, el semáforo. El semáforo es un tipo abstracto de datos caracterizado por: • Estructuras de datos: - Contador entero positivo - Cola de procesos esperando por ese semáforo. • Siendo s: semaforo, se pueden realizar las siguientes operaciones: - WAIT (s); - SIGNAL (s) - INIT (s, valor) WAIT y SIGNAL se excluyen en el tiempo. La operación INIT sólo está permitida en el cuerpo del programa. El comportamiento de estas operaciones es: WAIT (s) - Si contador = 0, se lleva la proceso que hizo la operación a la cola asociada con el semáforo s. Abandona el procesador a favor de otro proceso. Si contador > 0, se decrementa en 1 y el proceso continúa ejecutándose. Página 3 Programación Concurrente SIGNAL (s) - Si contador > 0, se incrementa en 1 y el proceso continúa. - Si contador = 0 y hay procesos esperando, se toma uno y se pone en estado de preparado para ejecutarse, el proceso que ejecutó la operación continúa. INIT (s, valor_inicial): - Pone el contador al valor indicado. Región crítica condicional Permite evitar el gasto de recursos que supone la espera activa de un proceso que espera una condición B que debe satisfacer una variable compartida v. La programación de una RCC es la siguiente: PROCEDURE P1 BEGIN ... REGION v DO AWAIT condición; ... END ... END; La diferencia con una RC es la sentencia AWAIT. Esta primitiva sólo puede estar dentro de una RC, si hay varias RC anidadas, AWAIT se asocia con la más próxima. Esta sentencia produce una espera pasiva y su funcionamiento es el siguiente: - Si condición = TRUE el proceso continúa por la siguiente sentencia. - Si condición = FALSE el proceso detiene su ejecución y abandona la RC pasando a una cola de espera Qs asociada con la RC. El proceso no vuelve a entrar en la RC hasta que otro proceso la abandona, es decir, cuando haya probabilidad de que ‘condición’ haya cambiado. Una RC tiene dos colas asociadas: • Qv : Cola de espera a una RC ocupada, es la cola de entrada a RC. • Qs : Cola de espera de los procesos que esperan un cambio en AWAIT. Buzones Los buzones se utilizan para comunicar dos procesos, la sintaxis es: VAR Identificador : Buffer Max of Mensaje Identificadores es el nombre de la variable buzón. La constante máximo indica la capacidad del buzón. Es decir, el máximo número de mensajes que puede albergar el buzón. El tipo mensaje, indica el tipo de mensajes que puede almacenar un buzón. El buzón es un tipo abstracto de datos compuesto por una estructura de datos y un conjunto de operaciones asociadas a tal estructura. Las operaciones que se realizan sobre un buzón son: SEND (mensaje, B): - Si el buzón B no está vacío, deja el mensaje y continua. - Si el buzón B está lleno, espera que halla un hueco, el proceso pierde el procesador. RECEIVE (mensaje, B): - Si el buzón B no está vacío, recoge el mensaje y continua. - Si el buzón B está vacío, espera un mensaje. Ambas operaciones se excluyen en el tiempo, son regiones críticas respecto del buzón. La semántica de los buzones implica las siguientes reglas de sincronización: 1. El productor no puede superar la capacidad del buzón 2. El consumidor no puede consumir mensajes más rápidamente de lo que se producen. 3. El orden en el que se consumen los mensajes es el mismo en el que se producen. Página 4 Programación Concurrente Los buzones así definidos tienen un comportamiento bloqueante, hay situaciones en las que esto no es deseable, la implementación de un buzón no bloqueante es: TYPE buzon_NO_bloqueante = SHARED RECORD Buffer: ARRAY [0..max-1] OF T p, c : 0..max-1; lleno : 0..max; END; PROCEDURE Enviar (mensaje:T; VAR b:buzon_NO_bloqueante; VAR hecho: BOOLEAN) BEGIN REGION b DO IF lleno = max THEN hecho : = FALSE ELSE BEGIN buffer(p) := mensaje; p:= (p+1) mod max; lleno := lleno + 1; hecho := TRUE; END END PROCEDURE Recibir (VAR mensaje :T; VAR b: buzón_NO_bloqueante; VAR hecho:BOOLEAN); BEGIN REGION b DO IF lleno = 0 THEN hecho : = FALSE ELSE BEGIN mensaje:= buffer(c); c:= (c+1) mod max; lleno := lleno - 1; hecho := TRUE; END END En esta implementación es responsabilidad del usuario del buzón decidir que hacer cuando la operación no se puede realizar. Existen aplicaciones que emplean buzones cuyo comportamiento es una combinación de bloqueante y no bloqueante, son los llamados buzones con time-out, en los que se indica el tiempo máximo que la operación puede bloquear el proceso. Sucesos Los sucesos son una herramienta que permite conseguir una gestión explícita de la cola de entrada o una RC diciendo quien puede entrar o quien no. Un suceso debe ir siempre asociado a una RC. La sintaxis es: VAR Identificador : Event Variable compartida Se puede declarar un suceso ev asociado a la variable compartida v como: VAR v : SHARED RECORD ... ev : EVENT v END; El suceso es un tipo abstracto de datos compuesto por una estructura de datos (una cola) y que soporta las siguientes operaciones: AWAIT (ev): El proceso que ejecuta esta operación abandona la RC donde se ejecutó, pasa a la cola del suceso ev asociado con dicha RC y se suspende, cediendo el procesador a otro proceso. Página 5 Programación Concurrente CAUSE (ev): Los procesos encolados en este proceso pasan a la cola principal de la RC y entraran en la misma cuando esté libre. Si no hay nadie en la cola del suceso esta operación no tiene efecto. En cualquier caso, el proceso que ejecuto la CAUSE continúa. Las dos operaciones anteriores son excluyentes en el tiempo y sólo se pueden ejecutar dentro de una RC. Puede haber varios sucesos asociados a una misma variable compartida. Si hay N sucesos, existen N+1 colas. Con esto el suceso se programa: VAR v: SHARED RECORD Recurso_disponible Recurso_pedido Turno : Event ... END; PROCEDURE Pide (..) BEGIN ... REGION v DO BEGIN WHILE no haya recursos disponibles DO BEGIN Pedir recurso AWAIT (Turno); END Toma recurso END PROCEDURE Produce () ... BEGIN REGION IF hay recursos disponibles THEN BEGIN Libera recurso CAUSE (Turno) END END END Se puede implementar una RCC utilizando sucesos. El constructor: REGION v DO BEGIN S1; AWAIT B; S2; END Se puede sustituir por REGION v DO BEGIN S1 WHILE NOT B DO AWAIT ($ev1); S2 CAUSE ($ev1); END Monitores Un monitor es un mecanismo que permite compartir de una manera fiable y efectiva tipos abstractos de datos entre procesos concurrentes. Un monitor proporciona: - Abstracción de datos - Exclusión mutua y mecanismos de sincronización entre procesos Página 6 Programación Concurrente Abstracción de los datos Un tipo abstracto de datos se caracteriza por un conjunto de declaraciones de variables cuyos valores definen el estado de una instancia de ese tipo, y por un conjunto de operaciones que actúan sobre dichas variables, en PASCAL esta estructura se consigue mediante la definición de una clase. Las variables declaradas en el interior sólo pueden ser accedidas mediante procedimientos declarados en la clase. Para diferenciar los procedimientos privados de la clase y los externos, a estos últimos se les añade la palabra reservada entry. Las clases se inicializan una sola vez a través de la sentencia init y sus variables locales permanecen todo el programa. Exclusión mutua y sincronización Además de la abstracción un monitor garantiza que el número de procesos que en un instante de tiempo están ejecutando el código del monitor es como máximo uno. Además el monitor proporciona un mecanismo de sincronización entre procesos, el constructor queue. Un procedimiento monitor puede retrasar a un proceso durante una cantidad arbitraria de tiempo ejecutando una operación delay sobre una variable del tipo queue. En un instante de tiempo sólo un proceso puede estar esperando en una queue. Cuando un proceso es retrasado por la operación delay, pierde el acceso al monitor (lo abandona) y suspende hasta que otro proceso lo reanude con una operación continue sobre la variable queue en la que se retrasó el primero. Si se permite que existan varios procesos encolados en una queue la operación continue despertará a uno de ellos dependiendo de la política de gestión: - FIFO: Evita el lockout - Mas prioridad: La operación delay precisa un parámetro adicional, puede provocar lockout. El ejemplo más claro del uso de monitores es el productor consumidor. PROGRAM Productor_Consumidor TYPE buffer = monitor (capacidad: INTEGER); (* implementa un buffer*) VAR contenido : ARRAY [0..MAX-1 ] OF TIPO in, out : 0...MAX-1; Cuantos_Hay : 0...MAX; pro, con : queue; (*...*) PROCEDURE lleno (): BOOLEAN; BEGIN RETURN Cuantos_Hay = Capacidad; END (*------------------------------*) PROCEDURE vacio (): BOOLEAN; BEGIN RETURN Cuantos_Hay=0; END; (*------------------------------*) PROCEDURE entry manda (mensaje:tipo) BEGIN IF lleno THEN delay (pro) contenido [in] : = mensaje in : = (in+1) mod capacidad; Cuantos_Hay : = Cuantos_Hay + 1 IF Cuantos_Hay = 1 THEN continue (con) END (*-----------------------------*) Página 7 Programación Concurrente PROCEDURE entry recibe (mensaje:tipo) BEGIN IF vacio THEN delay (con) mensaje : = contenido [in] out : = (out+1) mod capacidad; Cuantos_Hay : = Cuantos_Hay - 1 IF Cuantos_Hay = (capacidad-1) THEN continue (por) END (*-----------------------------*) BEGIN in : = 0 ; out : = 0 ; Cuantos_Hay : =0; END (* del monitor*) VAR Buzon : Buffer; PROCEDURE productor VAR m: Tipo; BEGIN REPEAT Elaborar mensaje (m) Buzon.manda (m); UNTIL fin END; (*-------------------------*) PROCEDURE Consumidor VAR m : Tipo; BEGIN REPEAT Buzon.recibe (m); Trabajar mensaje (m); UNTIL fin END; (*-------------------------*) BEGIN (* productor consumidor *) Init Buzón(20); COBEGIN Productor Consumidor COEND END; Sincronización por rendez-vous Rendez-vous significa sincronización e intercambio de información entre dos tareas (procesos en ADA) dadas, el hecho de establecerse rendez-vous se conoce como cita de tareas. Las tareas cuando interactúan primero se sincronizan y luego intercambian información y finalmente continuaran sus actividades por separado. Una tarea contiene puntos de sincronización llamados puntos de entrada. La sincronización entre dos tareas ocurre cuando una tarea llama a un punto de entrada y la otra la acepta estableciendo rendez-vous. La espera de las tareas cuando efectúan o esperan una cita es pasiva. La estructura de una tarea en ADA es la siguiente: Task Tarea is -- declaración de puntos de entrada Especificación -- otras declaraciones (interfaz) end Tarea Task body Tarea is begin -- implementación de las citas -- en los puntos de entrada -- otras cosas end Tarea Cuerpo (Parte oculta) Página 8 Programación Concurrente 3 Interbloqueos Definición de interbloqueo En un entorno de multiprogramación varios procesos compiten por un número finito de recursos. Si un proceso pide un recurso que en ese momento no está disponible entra en espera. Si el proceso A tiene el recurso R1 y espera el R2, mientras que el proceso B tiene el recurso R2 y espera el R1, ninguna de las dos peticiones podrá ser satisfecha y ambos procesos se bloquearán. Un término relacionado con el interbloqueo es la inanición (starvion) que denota el estado en que uno o varios procesos son retrasados indefinidamente en el acceso a un recurso a favor de otros. Ejemplos de interbloqueos Suponiendo siempre una situación COBEGIN P1; P2 COEND; Regiones críticas P1 ... ... REGION r DO BEGIN ... REGION s DO BEGIN ... END (s) ... END (r ) P2 ... ... REGION s DO BEGIN ... REGION r DO BEGIN ... END ( r) ... END (s) Semáforo en el interior de una región crítica. VAR r: SHARED ...; S: SEMAPHORE; (* INIT (s,0)*) P1 ... ... REGION r DO BEGIN ... WAIT(s); ... ... END P2 ... ... REGION r DO BEGIN ... SIGNAL (s) ... ... END Con Región Crítica Condicional. VAR r: SHARED RECORD C1, C2: BOOLEAN (*INIT FALSE*) END; P1 ... ... REGION r DO BEGIN ... C1:=TRUE; AWAIT C2 ... END; P2 ... ... REGION r DO BEGIN ... AWAIT C1 C2 := TRUE; ... END; Página 9 Programación Concurrente Con semáforos VAR SEMAPHORE; (*INIT (s,0)*) P1 ... ... WAIT (s) ... SIGNAL (s) ... END; P2 ... ... WAIT (s); ... SIGNAL (s); ... END; Con buzones VAR b1, b2: buffer 3 OF INTEGER; P1 ... ... FOR i : = 1 TO 4 DO SEND (m,b1) ... FOR i : = 1 TO 4 DO SEND (m,b2) ... END; P2 ... ... FOR i : = 1 TO 4 DO RECEIVE (m,b2); ... FOR i : = 1 TO 4 DO RECEIVE (m,b1); ... END; Con sucesos VAR v: SHARED RECORD ... sc : EVENT r END; P1 REGION r DO BEGIN ... CAUSE(sc); ... AWAIT C2 ... END; P2 REGION r DO BEGIN ... AWAIT (sc); ... CAUSE (sc); ... END; Recursos La secuencia de operaciones para usar un recurso son: - Pedir recurso. Si no está disponible el proceso se bloquea. Utilización del recurso. Liberación del recurso. Tanto la petición como la liberación del recurso son llamadas al sistema operativo, que es el que mantiene el estado de cada uno de ellos. Se puede distinguir entre recursos permanentes y temporales. Un proceso permanente puede ser utilizado repetidamente por muchos procesos (dispositivos físicos). Un proceso temporal es producido por un proceso y consumido por otro (mensajes). Definición y caracterización de los interbloqueos Un conjunto de procesos está interbloqueado si cada proceso en el conjunto está esperando por un suceso que solamente puede producir otro proceso del conjunto. Puesto que todos los procesos esperan, ninguno de ellos podrá producir el suceso que despierte a cualquiera de los otros miembros del conjunto. Página 10 Programación Concurrente Condiciones necesarias. Los interbloqueos se previenen asegurando que no se cumple nunca una o más de las condiciones siguientes: - Exclusión mutua: cada recurso o bien está disponible o bien asignado a un único proceso. Asignación parcial: Un proceso puede adquirir sus recursos por partes. Programación no expulsora: Un recurso sólo puede ser liberado por el proceso que lo ha adquirido. Espera circular: Debe existir un conjunto de procesos esperando (P0, P1, ...,Pn) tal que P0 está esperando un recurso que tiene P1, P1 uno que tiene P2;...P(n-1) espera uno que tiene Pn y Pn el de P0. Grafos de asignación de recursos Los interbloqueos se pueden describir mediante un grafo dirigido, el grafo de asignación de recursos de sistema. El grafo está compuesto por un conjunto de nodos compuestos por los procesos y un conjunto de arcos dirigidos que unen procesos y recursos. Los procesos se representan mediante círculos, los recursos mediante rectángulos. Un arco dirigido de Pi a Ri indica que Pi está bloqueado esperando Ri. Si el arco está dirigido de Ri a Pi significa que Pi tiene asignado el recurso Ri. Si cada recurso tiene una instancia la existencia de un ciclo en el grafo es condición necesaria y suficiente para que exista interbloqueo. Si cada recurso tiene varias instancias, un ciclo no implica necesariamente la existencia de un interbloqueo. Estrategias para tratar los interbloqueos Ignorar el problema. Evidentemente es la solución más simple, se puede defender desde el punto de vista económico de intentar tratarlos si se van a producir pocos. Asegurar que el sistema nunca entra en estado de interbloqueo. Se trata de imponer restricciones a los procesos para que los interbloqueos no puedan producirse, existen dos métodos: - Prevenir los interbloqueos, es decir, asegurar que no se producen. - Evitar los interbloqueos Técnicas para prevenir los interbloqueos Están basadas en que no se lleguen a producir las condiciones necesarias para que se produzca un interbloqueo. Exclusión mutua. La exclusión mutua debe mantenerse para tipos de recursos no compartibles, ya que los compartibles no pueden verse envueltos en un interbloqueo. En general, no es posible eliminar la exclusión mutua. Asignación parcial. Para eliminar esta condición se debe asegurar que cuando un proceso solicite un recurso no tenga asignado otro. Existen dos protocolos: - Asignación total: El proceso antes de ejecutarse solicita todos los recursos que precisa. Si estos están disponibles el proceso se ejecuta hasta terminar y los libera. Si no están disponibles el proceso se espera a que lo estén. El problema es que muchos procesos no conocen a priori que recursos precisan, además los recursos no se administran de manera óptima. - Un proceso puede solicitar recursos sólo cuando no tenga otros asignados, antes de solicitar cualquier recurso debe liberar los que tiene asignados. Ambas soluciones padecen un posible starvation, inanición. Programación no expulsora. Eliminar esta condición significa obligar a los procesos a que liberen sus recursos temporalmente a favor de otros procesos. Para ello se puede seguir este protocolo: Si un proceso que tiene asignado un número determinado de recursos pide otro(s) que no se le puede(n) asignar inmediatamente, libera todos los recursos asignados. El proceso arranca cuando se le asignan todos los procesos que necesita. Página 11 Programación Concurrente Otro método es comprobar si el recurso necesario está disponible, entonces se asigna, por el contrario, si lo tiene otro proceso se comprueba si éste está esperando otro recurso, en cuyo caso se expulsa el recurso asignándose al proceso que lo pedía. Si el recurso no está disponible ni está asignado a un proceso bloqueado, el proceso solicitante se suspende. Mientras se mantenga en este estado los recursos que dispone se le pueden quitar. El proceso se reanuda cuando disponga del recurso que solicitó y de todos aquellos que se le hayan quitado mientras estaba bloqueado. La expulsión no es práctica en los dispositivos que necesitan intervención del operador como son los discos, cintas, etc. Espera circular. La espera circular se puede eliminar imponiendo una ordenación lineal de los tipos de recursos. Todo proceso puede pedir recursos siempre que lo desee, pero todas las peticiones deben realizarse en orden numérico creciente. Es lo que se denomina asignación jerárquica de recursos. Con estas suposiciones no puede ocurrir interbloqueo, una petición de recursos al nivel más alto Lmax, no puede ser retrasado por peticiones de otro nivel superior por que este no existe, cuando esta liberación se produzca, se libera para Lmax-1. El punto débil es que puede llegar a ser imposible encontrar una ordenación que satisfaga a todos los usuarios. Técnicas para evitar los interbloqueos En los sistemas en los que no se pueden eliminar las condiciones que evitan los interbloqueos se debe de tener alguna información adicional sobre los recursos que utilizará el proceso. El modelo más sencillo y utilizado es exigir que cada proceso diga antes de ser arrancado, cuantos recursos necesitará a lo largo de su ejecución. El algoritmo más utilizado para evitar los interbloqueos es el denominado ‘algoritmo del banquero’, que generalizado a n recursos, sabiendo las necesidades máximas por adelantado se construyen dos tablas, la de recursos asignados y la de necesidades de recursos. Dada una petición de recursos, el algoritmo simula concederla y comprueba si esto lleva a una situación segura o no de la siguiente manera A B C D E Recursos asignados R1 R2 R3 R4 3 0 1 1 0 1 0 0 1 1 1 0 1 1 0 1 0 0 0 0 Recursos existentes : Recursos asignados: Recursos disponibles: 1. 2. 3. Necesidades de recursos R1 R2 R3 R4 A 1 1 0 0 B 0 1 1 2 C 3 1 0 0 D 0 0 1 0 E 2 1 1 0 E : (6,3,4,2) A: (5,3,2,2) D: (1,0,2,0) Buscar en la tabla ‘necesidades de recursos’, una fila R cuyas necesidades de recursos son más pequeñas que el vector D. Si esta fila no existe indica que el sistema está interbloqueado. Encontrada la fila R, se supone que el proceso de esa fila pide todos los recursos, termina y los libera sumándose dichos recursos al vector D. Se repite 1 y 2 hasta que los procesos terminan, situación segura, o se produce interbloqueo, situación insegura. El algoritmo del banquero en la práctica no es factible por: - Coste en tiempo de ejecución. - Pocos procesos conocen sus necesidades máximas a priori Permitir los interbloqueos Si el sistema no emplea ningún protocolo para evitar los interbloqueos, debe implementar un esquema de detección y recuperación de los mismos. En esta técnica, el sistema monitoriza las peticiones y liberaciones de recursos. Cada vez que se concede o libera un recurso, se actualiza el grafo de recursos y se comprueba si existe interbloqueo o no. Si existe se elimina uno de los procesos que intervienen, si el problema no se soluciona eliminamos otro, así hasta que se elimine el interbloqueo. Página 12 Programación Concurrente Interbloqueos con recursos temporales. Varios procesos productores y consumidores conectados a un único buzón no pueden interbloquearse, pero si se conectan circularmente varios procesos mediante buzones puede existir interbloqueo. Para evitar la circularidad se puede jerarquizar los procesos en niveles. Cada proceso pertenece a un nivel Li. Los procesos de niveles inferiores se denominan maestros y pueden proporcionar mensajes (‘peticiones’) a los procesos de niveles superiores, seguidores. Estos pueden responder (‘replicas’) a sus maestros en contestación a sus peticiones. Con esta configuración las peticiones se envían en un sentido y las respuestas en el contrario. Peticiones ....... P1 Pn-1 P2 Pn ......... Respuestas A pesar de esta jerarquía el sistema aún puede interbloquearse. Dos procesos P y Q pueden ser incapaces de mandar mensajes y respuestas porque ambos buzones estén llenos o ser incapaces de recibirlos si los buzones están vacíos, con lo que quedarían interbloqueados. P Q La regla que soluciona el problema es “no intentar mandar un mensaje o respuesta a menos que alguien lo vaya a recibir; o no intentar recibir un mensaje o respuesta al menos que alguien lo vaya a mandar”. Con esta solución no existen interbloqueos con comunicación de procesos jerarquizados. 1. La hipótesis es cierta para cualquier proceso Pj del nivel más alto (j=max) ya que no existen seguidores que lo puedan retrasar. 2. Un proceso Pi-1 del nivel Li-1 satisface la hipótesis si no es retrasado indefinidamente por un seguidor Pj de un nivel superior, tenemos dos posibilidades: - Pi-1 es incapaz de mandar un mensaje a un seguidor Pj porque el buzón está lleno. Según la hipótesis el seguidor recibirá finalmente el correspondiente mensaje y permitirá seguir a Pi-1. - Pi-1 es incapaz de recibir una respuesta de un seguidor Pj porque un buzón está vacío, según la hipótesis el seguidor del nivel Lj (j ≥ i) recibirá el mensaje y producirá la respuesta que permita continuar a Pi-1. 3. Por inducción se demuestra que se cumplen de L1 a Lmax Página 13