Está en la página 1de 8

EXCLUSIÓN MUTUA

En un sistema multiprogramado con un único procesador, los procesos se intercalan en el tiempo


(i.e. Round Robín) para dar apariencia de ejecución simultánea. Aunque no se consigue un
procesado en paralelo real, y aunque se produce un sobrecargado en la CPU por el hecho de tener
que cambiar de tarea constantemente, las ventajas de todo esto son muy elevadas. Ejemplo:
avion-torre, chat's, etc.

Uno de los grandes problemas que nos podemos encontrar es que el hecho de compartir recursos
está lleno de riesgos. Por ejemplo, si dos procesos hacen uso al mismo tiempo de una variable
globaly ambos llevan a cabo tanto operaciones de lectura como de escritura sobre dicha variable,
el orden en que se ejecuten estas lecturas y escrituras es crítico, puesto que se verá afectado el
valor de la variable.

Solución a la Exclusión Mutua: Semáforo


Semáforos.

Semáforos binarios.

Dijkstra dio en 1968 una solución elegante y sencilla al problema de la exclusión mutua con la
introducción del concepto de semáforo binario. Esta técnica permite resolver la mayoría de los
problemas de sincronización entre procesos y forma parte del diseño de muchos sistemas
operativos y de lenguajes de programación concurrentes.

Un semáforo binario es un indicador de condición (S) que registra si un recurso está disponible o
no. Un semáforo binario sólo puede tomar dos valores: O y 1. Si, para un semáforo binario. S=1
entonces el recurso está disponible y la tarea lo puede utilizar; si S=0 el recurso no está disponible
y el proceso debe esperar.

Los semáforos sólo permiten tres operaciones sobre ellos:

Espera.

Señal.

Inicializar.

wait (variable)

begin

while (variable)>=0 do {esperar}

variable=variable-1

end
signal (variable)

begin

variable=variable+1

end

El wait y el signal son indivisibles y no se pueden interrumpir, es decir, no se pueden ejecutar


ambas a la vez.

Los semáforos binarios los utilizaremos para recursos de una sola instancia, y para sincronización
binaria.

Semáforos generales.

El semáforo binario resulta adecuado cuando hay que proteger un recurso que pueden compartir
varios procesos, pero cuando lo que hay que proteger es un conjunto de recursos similares, se
puede usar una versión más general del concepto de semáforo que lleve la cuenta del número de
recursos disponibles. En este caso el semáforo se inicializa con el número total de recursos
disponibles (n) y las operaciones de espera y señal se diseñan de modo que se impida el acceso al
recurso protegido por el semáforo cuando el valor de éste es menor o igual que cero.

Cada vez que se solicita y obtiene un recurso, el semáforo se decrementa y se incrementa cuando
se libera uno de ellos. Si la operación de espera se

ejecuta cuando el semáforo tiene un valor menor que uno, el proceso debe quedar en espera de
que la ejecución de una operación señal libere alguno de

los recursos.

Al igual que en los semáforos binarios, las ejecuciones de las operaciones son indivisibles, esto es,
una vez que se ha empezado la ejecución de uno de estos

procedimientos se continuará hasta que la operación se haya completado.

Solución a la Exclusión Mutua: Monitores


Monitores.

Un monitor es una estructura formada por una cabecera que los identifica, un conjunto de
variables globales a todos los procedimientos del monitor, un conjunto de procedimientos y un
bloque de inicialización, el cual se ejecuta una única vez, cuando se crea el monitor.

{ nombre
{ variables globales accesibles desde todos los procedimientos

{ procedimiento A

{ procedimiento B

{ inicialización (solo se ejecuta una vez)

El recurso que queremos compartir (S.C.) se declara como monitor y en él se incluyen todas las
operaciones que afecten a dicho recurso. Los procesos que usan el monitor se sitúan de forma
independiente y cuando deseen usar el recurso, llamarán al procedimiento del monitor que
implemente la operación que desea ejecutar. Para resolver la sincronización se utilizan dos
instrucciones, wait (bloquea siempre) y signal, y actúan sobre variables condición que son colas y
además no tienen valor.

cwait (c): suspende la ejecución del proceso que llama bajo la condición c. El monitor está ahora
disponible para ser usado por otros procesos.

csignal(c): reanuda la ejecución de algún proceso suspendido después de un cwait bajo la misma
condición. Si hay varios procesos, se elige uno de ellos; si no hay ninguno, no hace nada.

Las colas son un tipo de variables que son el lugar donde almacenaremos los procesos que hayan
quedado suspendidos bajo un. wait. y estén a la espera de ser "despertados".

En el caso de que dentro de un monitor un proceso se quede bloqueado con un wait por lo cual se
bloquea y no dejaría ejecutarse a otro proceso, el SO despierta a otro proceso cuya última
instrucción haya sido un signal y además vale la condición que hace que se bloqueen los procesos.

Habrá algunos casos en los que nos apoyemos en una variable booleana para garantizar la
exclusión mutua sobre una variable con una sola instancia. También habrá casos en los que
utilicemos un contador.

Solución a la Exclusión Mutua: Algoritmo de


Dekker
Solución que garantiza la exclusión mutua al gestionar elegantemente este proceso.

Utiliza, además de las banderas de intención de entrar, un variable turno (proceso favorecido) que
resuelve en caso de conflicto.
Da la posibilidad de que el otro proceso entre en su sección crítica una segunda vez si lo hace
antes que el proceso que acaba de dejar el bucle de espera, asigne el valor cierto a su intención de
entrar. Pero cuando de nuevo tiene el procesador, realiza la asignación y es su turno, así que se
ejecuta.

En consecuencia, no produce aplazamiento indefinido.

La solución se desarrolla por etapas. Este método ilustra la mayoría de los errores habituales que
se producen en la construcción de programas concurrentes.

Primer intento

Cualquier intento de exclusión mutua debe depender de algunos mecanismos básicos de exclusión
en el hardware. El más habitual es que sólo se puede acceder a una posición de memoria en cada
instante, teniendo en cuenta esto se reserva una posición de memoria global llamada turno. Un
proceso que desea ejecutar su sección crítica primero evalúa el contenido de turno. Si el valor de
turno es igual al número del proceso, el proceso puede continuar con su sección crítica. En otro
caso el proceso debe esperar. El proceso en espera, lee repetitivamente el valor de turno hasta
que puede entrar en su sección crítica. Este procedimiento se llama espera activa.

Después de que un proceso accede a su sección crítica y termina con ella, debe actualizar el valor
de turno para el otro proceso.

Segundo intento:

Cada proceso debe tener su propia llave de la sección crítica para que, si uno de ellos falla, pueda
seguir accediendo a su sección crítica; para esto se define un vector booleano señal. Cada proceso
puede evaluar el valor de señal del otro, pero no modificarlo. Cuando un proceso desea entrar en
su sección crítica, comprueba la variable señal del otro hasta que tiene el valor falso (indica que el
otro proceso no está en su sección crítica). Asigna a su propia señal el valor cierto y entra en su
sección crítica. Cuando deja su sección crítica asigna falso a su señal.

Si uno de los procesos falla fuera de la sección crítica, incluso el código para dar valor a las
variables señal, el otro proceso no se queda bloqueado. El otro proceso puede entrar en su
sección crítica tantas veces como quiera, porque la variable señal del otro proceso está siempre en
falso. Pero si un proceso falla en su sección crítica o después de haber asignado cierto a su señal,
el otro proceso estará bloqueado permanentemente.
Tercer intento

El segundo intento falla porque un proceso puede cambiar su estado después de que el otro
proceso lo ha comprobado, pero antes de que pueda entrar en su sección crítica.

Si un proceso falla dentro de su sección crítica, incluso el código que da valor a la variable señal
que controla el acceso a la sección crítica, el otro proceso se bloquea y si un proceso falla fuera de
su sección crítica, el otro proceso no se bloquea.

Si ambos procesos ponen sus variables señal a cierto antes de que ambos hayan ejecutado una
sentencia, cada uno pensará que el otro ha entrado en su sección crítica, generando así un
interbloqueo.

Cuarto intento

En el tercer intento, un proceso fijaba su estado sin conocer el estado del otro. Se puede arreglar
esto haciendo que los procesos activen su señal para indicar que desean entrar en la sección
crítica, pero deben estar listos para desactivar la variable señal y ceder la preferencia al otro
proceso.

Existe una situación llamada bloqueo vital, esto no es un interbloqueo, porque cualquier cambio
en la velocidad relativa de los procesos rompería este ciclo y permitiría a uno entrar en la sección
crítica. Recordando que el interbloqueo se produce cuando unos conjuntos de procesos desean
entrar en sus secciones críticas, pero ninguno lo consigue. Con el bloqueo vital hay posibles
secuencias de ejecución con éxito.

Una solución correcta

Hay que observar el estado de ambos procesos, que está dado por la variable señal, pero es
necesario imponer orden en la actividad de los procesos para evitar el problema de "cortesía
mutua". La variable turno del primer intento puede usarse en esta labor, indicando que proceso
tiene prioridad para exigir la entrada a su sección crítica.

Solución a la Exclusión Mutua: Algoritmo de


Peterson
Simplifica el algoritmo de Decker.

El protocolo de entrada es más elegante con las mismas garantías de exclusión mutua,
imposibilidad de bloqueo mutuo y de aplazamiento indefinido.
El algoritmo de Peterson es un algoritmo de programación concurrente para exclusión mutua, que
permite a dos o más procesos o hilos de ejecución compartir un recurso sin conflictos, utilizando
sólo memoria compartida para la comunicación.

El algoritmo de Decker resuelve el problema de la exclusión mutua, pero mediante un programa


complejo, difícil de seguir y cuya corrección es difícil de demostrar. Peterson ha desarrollado una
solución simple y elegante. Como antes, la variable global señal indica la posición de cada proceso
con respecto a la exclusión mutua y la variable global turno resuelve los conflictos de
simultaneidad.

Considérese el proceso P0. Una vez que ha puesto señal [0] a cierto, P1 no puede entrar en su
sección crítica. Si P1 esta aun en su sección crítica, entonces señal [1] = cierto y P0 está bloqueado
en su bucle while. Esto significa que señal [1] es cierto y turno = 1. P0 puede entrar en su sección
crítica cuando señal [1] se ponga a falso o cuando turno se ponga a 0. Considérense ahora los
siguientes casos exhaustivos:

P1 no está interesado en entrar en su sección crítica. Este caso es imposible porque implica que
señal [1] = falso.

P1 está esperando entrar en su sección crítica. Este caso es también imposible porque si turno = 1,
P1 podría entrar en su sección crítica.

P1 entra en su sección crítica varias veces y monopoliza el acceso a ella. Esto no puede pasar
porque P1 está obligado a dar a P0 una oportunidad poniendo turno a 0 antes de cada intento de
entrar en su sección crítica.

Así pues, se tiene una solución posible al problema de la exclusión mutua para dos procesos. Es
más, el algoritmo de Peterson se puede generalizar fácilmente al caso de n procesos.

Fundamento del Algoritmo de Peterson

Cuando dos o más procesos secuenciales en cooperación ejecutan todos ellos asíncronamente y
comparten datos comunes se produce el problema de la sección crítica.

Un proceso productor genera información que es utilizada por un proceso consumidor. Para que
los procesos productores y consumidores ejecuten concurrentemente, tenemos que creer un
conjunto de buffers que puedan ser alimentados por el productor y vaciados por el consumidor.

El consumidor tiene que esperar si todos los buffers se encuentran vacíos, y el productor tiene que
esperar si todos los buffers están llenos.

Una solución simple al problema de la sección crítica nos la proporciona Peterson. Esta solución es
básicamente una combinación del Algoritmo que asocia cada proceso con su variable de cerradura
y de una pequeña modificación del Algoritmo de Alternancia Estricta.
Interbloqueo
A cada proceso se le asignan varios recursos para su ejecución. En sistemas de
multiprogramación, uno de los principales objetivos del sistema operativo es el compartimiento de
los recursos. Cuando se comparten los recursos entre una población de usuarios, cada uno de los
cuales mantiene un control exclusivo sobre ciertos recursos asignados a él, es posible que otros
usuarios no terminen sus procesos (bloqueo entre usuarios).

El bloqueo mutuo puede aparecer de muchas formas:

- Si a un proceso se le asigna la tarea de esperar a que ocurra un evento y el sistema no


incluye providencias para señalar la ocurrencia de dicho evento, habrá un bloqueo mutuo con un
solo proceso. Es muy difícil detectar bloqueos mutuos de esta naturaleza.

- La mayor parte de los bloqueos mutuos en sistemas reales implican una competencia entre
varios procesos por varios recursos.

Bloqueo mutuo = Deadlock = Abrazo mortal

El bloqueo mutuo o abrazo mortal (deadlock) es el problema más serio que se puede presentar en
un ambiente de multiprogramación. Es la actividad en que dos o más procesos esperan un
recurso (CPU, memoria, entrada/salida) que nunca les va a ser otorgado.

Bloqueo mutuo

Como se ve, aquí el sistema está bloqueado: Proceso 1 tiene a Recurso 1 y necesita a Recurso 2
para continuar. Y Proceso 2 tiene asignado a Recurso 2, y necesita a Recurso 1 para continuar.
Cada proceso espera que el otro libere un recurso que no liberará hasta que el otro libere su
recurso, lo cual no sucederá si el primero no libera su recurso. Aquí se presenta una espera
circular.
Bloqueo

Otro ejemplo, dos procesos adueñados de CPU. Proceso A se ejecuta y llama a Proceso B, Proceso
B se ejecuta y llama a Proceso A, ..., si llega otro proceso, no lo van a dejar entrar a CPU a
ejecutarse.

También podría gustarte