SISTEMAS OPERATIVOS
UNIDAD 3: CONCURRENCIA
Licenciatura en Ciencias de la Computación
Licenciatura en Sistemas de Información
Tecnicatura Universitaria en Programación Web
Departamento de Informática
Facultad de Ciencias Exactas, Físicas y Naturales
Universidad Nacional de San Juan
Esquema de contenidos
1. PRINCIPIOS DE LA CONCURRENCIA 2
1.1. Condición de carrera o competencia 3
1.2. Preocupaciones del sistema operativo 4
1.3. Interacción de procesos 5
1.3.1. Competencia entre los procesos por los recursos 6
1.3.2. Cooperación entre procesos vía memoria compartida 7
1.3.3. Cooperación entre procesos vía comunicación 8
1.4. Requisitos para implementar la exclusión mutua 8
2. EXCLUSIÓN MUTUA 10
2.1. Soporte Hardware: Deshabilitar interrupciones 10
2.2. Soporte Hardware: Instrucciones hardware especiales 11
2.3. Soporte Software: Semáforos 12
2.3.1. Definición de semáforo 12
2.3.2. Semáforos Binarios 12
2.3.3. Semáforos Contadores 13
2.3.4. El problema del barbero dormilón 14
2.4. Soporte Software: Monitores 15
2.5. Soporte Software: Paso de mensajes 16
3. BIBLIOGRAFÍA 17
1. PRINCIPIOS DE LA CONCURRENCIA
Como definición se dirá que la concurrencia es el intento de acceso simultáneo a un
recurso no compartible por parte de dos o más procesos (una variable, un dato, una
celda de memoria, periférico dedicado).
Es decir, que la concurrencia se produce a partir de la existencia de varias actividades
simultáneas o paralelas. Por ejemplo, la superposición de las operaciones de E/S con
la ejecución del cómputo.
La concurrencia lleva asociado los siguientes problemas:
● Conmutar de una tarea a otra
● Proteger una determinada actividad de los efectos de otra
● Sincronizar las tareas que sean mutuamente dependientes
En un sistema multiprogramado de procesador único, los procesos se entrelazan en el
tiempo para ofrecer la apariencia de ejecución simultánea. Aunque no se consigue
procesamiento paralelo real, el ir cambiando de un proceso a otro supone cierta
sobrecarga, la ejecución entrelazada proporciona importantes beneficios en la
eficiencia del procesamiento y en la estructuración de los programas. En un sistema de
múltiples procesadores no sólo es posible entrelazar la ejecución de múltiples procesos
sino también solaparlas (paralelismo real).
En primera instancia, puede parecer que el entrelazado y el solapamiento representan
modos de ejecución fundamentalmente diferentes y que presentan diferentes
problemas. De hecho, ambas técnicas pueden verse como ejemplos de procesamiento
concurrente y ambas presentan los mismos problemas. En el caso de un
monoprocesador, los problemas surgen de una característica básica de los sistemas
multiprogramados: no se puede predecir la velocidad relativa de ejecución de los
procesos. Ésta depende de la actividad de los otros procesos, de la forma en que el SO
maneja las interrupciones y de las políticas de planificación del SO. Se plantean las
siguientes dificultades:
1. La compartición de recursos globales está cargada de peligros. Por ejemplo, si
dos procesos utilizan ambos la misma variable global (en el ejemplo Aux) y
ambos realizan lecturas y escrituras sobre esa variable, entonces el orden en
que se ejecuten las lecturas y escrituras es crítico (esto se conoce como
velocidad relativa de los procesos).
2. Para el SO es complicado gestionar la asignación de recursos de manera
óptima. Por ejemplo, el proceso A puede solicitar el uso de un periférico de
E/S, y serle concedido el control, y luego ser suspendido justo antes de
utilizarlo.
3. Llega a ser muy complicado localizar errores de programación porque los
resultados son típicamente no deterministas y no reproducibles.
Todas las dificultades precedentes se presentan también en un sistema
multiprocesador, porque aquí tampoco es predecible la velocidad relativa de ejecución
de los procesos. Un sistema multiprocesador debe bregar también con problemas
derivados de la ejecución simultánea de múltiples procesos. Sin embargo,
fundamentalmente los problemas son los mismos que aquéllos de un sistema
monoprocesador. Esto quedará claro a medida que la exposición avance.
1.1. Condición de carrera o competencia
La condición de carrera sucede cuando múltiples procesos o hilos leen y escriben
datos de manera que el resultado final depende del orden de ejecución de las
instrucciones en los múltiples procesos.
La condición de carrera o competencia está estrechamente vinculado al concepto de
“velocidad relativa” de los procesos.
A modo de primer ejemplo puede revisar el esquema anterior. Los procesos A y B
comparten la variable global Aux. En algún punto de su ejecución, tanto A como B
actualizan Aux. Así, ambos procesos compiten en una carrera por escribir la variable
Aux. En este ejemplo el “perdedor” de la carrera (el proceso que actualiza el último)
determina el valor de Aux.
Como segundo ejemplo, considere dos procesos, C y D, que comparten las variables
globales e y f, con valores iniciales e = 1 y f = 2. En algún punto de su ejecución, C
ejecuta la asignación e = e + f y, en algún punto de su ejecución, D ejecuta la
asignación f = e + f. Note que los dos procesos actualizan diferentes variables. Sin
embargo, los valores finales de las dos variables dependen del orden en que los dos
procesos ejecuten estas dos asignaciones. Si C ejecuta su sentencia de asignación
primero, entonces los valores finales serán e = 3 y f = 5. Si D ejecuta su sentencia de
asignación primero, entonces los valores finales serán e = 4 y f = 3.
1.2. Preocupaciones del sistema operativo
Estos son algunos aspectos de diseño y gestión que pueden enumerarse que surgen por
la existencia de la concurrencia:
1. El SO debe ser capaz de saber detalladamente la situación de todos los procesos
del sistema. Esto se consigue con el uso de los BCP descriptos.
2. El SO debe asignar y desasignar recursos para cada proceso activo. Estos
recursos incluyen:
a. Procesador
b. Memoria
c. Archivos
d. Dispositivos de E/S
3. El SO debe proteger los datos y recursos físicos de cada proceso frente a
interferencias involuntarias de otros procesos. Esto involucra técnicas que
relacionan memoria, archivos y dispositivos de E/S.
4. El funcionamiento de un proceso y el resultado que produzca, debe ser
independiente de la velocidad a la que suceda su ejecución en relación con la
velocidad de otros procesos concurrentes.
Para entender cómo puede abordarse la cuestión de la independencia de la velocidad,
es necesario ver las formas en que los procesos pueden interaccionar.
1.3. Interacción de procesos
Se puede clasificar las formas en que los procesos interaccionan en base al grado en
que perciben la existencia de cada uno de los otros en:
● Procesos que no se perciben entre sí. Son procesos independientes que no se
pretende que trabajen juntos. El mejor ejemplo de esta situación es la
multiprogramación de múltiples procesos independientes. Estos bien pueden
ser trabajos por lotes o bien sesiones interactivas o una mezcla. Aunque los
procesos no estén trabajando juntos, el SO necesita preocuparse de la
competencia por recursos. Por ejemplo, dos aplicaciones independientes
pueden querer ambas acceder al mismo disco, archivo o impresora. El sistema
operativo debe regular estos accesos.
● Procesos que se perciben indirectamente entre sí. Son procesos que no están
necesariamente al tanto de la presencia de los demás, pero que comparten
accesos a algún objeto, como un buffer de E/S o una posición de memoria.
Tales procesos exhiben cooperación en la compartición del objeto común.
● Procesos que se perciben directamente entre sí. Son procesos capaces de
comunicarse entre sí vía el ID del proceso y que son diseñados para trabajar
conjuntamente en cierta actividad. De nuevo, tales procesos exhiben
cooperación.
Grado de percepción Relación Influencia que un proceso tiene Potenciales
sobre el otro problemas de control
Procesos que no se Competencia - Los resultados de un proceso * Exclusión mutua
perciben entre sí son independientes de la acción * Interbloqueo
de los otros * Inanición
- La temporización del proceso
puede verse afectada
Procesos que se Cooperación - Los resultados de un proceso * Exclusión mutua
perciben por pueden depender de la * Interbloqueo
indirectamente entre sí compartición información obtenida de otros. * Inanición
(por ejemplo, objeto de memoria - La temporización del proceso * Coherencia de
compartido) puede verse afectada datos
Procesos que se Cooperación - Los resultados de un proceso * Interbloqueo
perciben directamente por pueden depender de la * Inanición
entre sí (tienen comunicación información obtenida de otros.
primitivas de - La temporización del proceso
comunicación a su puede verse afectada
disposición)
Las condiciones no serán siempre tan claras como se sugiere en la tabla. En ocasiones,
algunos procesos pueden exhibir aspectos tanto de competición como de cooperación.
No obstante, es constructivo examinar cada uno de los tres casos de la lista y
determinar sus implicaciones para el SO.
1.3.1. Competencia entre los procesos por los recursos
Los procesos concurrentes entran en conflicto entre ellos cuando compiten por el uso
del mismo recurso. En su forma pura, puede describirse la situación como sigue. Dos
o más procesos necesitan acceso a un recurso durante el curso de su ejecución. Ningún
proceso se percata de la existencia de los otros procesos y ninguno debe verse afectado
por la ejecución de los otros procesos. Esto conlleva que cada proceso debe dejar
inalterado el estado de cada recurso que utilice. Ejemplos de recursos son los
dispositivos de E/S, la memoria, el tiempo de procesador y el reloj.
No hay intercambio de información entre los procesos en competencia. No obstante, la
ejecución de un proceso puede afectar al comportamiento de los procesos en
competencia. En concreto, si dos procesos desean acceder al mismo recurso único,
entonces, el SO reservará el recurso para uno de ellos, y el otro tendrá que esperar. Por
lo tanto, el proceso al que se le deniega el acceso será ralentizado. En un caso
extremo, el proceso bloqueado puede no conseguir nunca el recurso y por tanto no
terminar nunca satisfactoriamente.
En el caso de procesos en competencia, deben afrontarse tres problemas de control.
Primero está la necesidad de exclusión mutua. Supóngase que dos o más procesos
requieren acceso a un recurso único no compartible, como una impresora. Durante el
curso de la ejecución, cada proceso estará enviando mandatos al dispositivo de E/S,
recibiendo información de estado, enviando datos o recibiendo datos. Se hará
referencia a tal recurso como un recurso crítico, y a la porción del programa que lo
utiliza como la sección crítica del programa. Es importante que sólo se permita un
programa por vez en su sección crítica (conjunto de instrucciones, dentro de un
programa, que manipulan un recurso crítico). En el caso de una impresora, por
ejemplo, es necesario que cualquier proceso individual tenga el control de la
impresora mientras imprime el archivo completo. De otro modo, las líneas de los
procesos en competencia se intercalarían.
La aplicación de la exclusión mutua crea dos problemas de control adicionales. Uno es
el del interbloqueo, tema que se verá más adelante, pero a modo introductorio se dirá
que es cuando dos o más de procesos quedan bloqueados de manera permanente
porque esperan eventos que nunca sucederán.
Un último problema de control es la inanición. Por inanición se entiende la falta
extrema de alimento. En el caso de un proceso el alimento es el procesador que le
permite ejecutar sus instrucciones y en algún momento terminar. Entonces se entiende
por inanición de un proceso a la espera infinita del uso del procesador ya que el
planificador selecciona siempre a otros procesos antes para su ejecución.
El control de la competencia involucra inevitablemente al SO ya que es quien
distribuye los recursos y cualquier solución lo involucra como, por ejemplo,
proporcionar un servicio de bloqueo.
Para comunicar procesos cooperativos existen diversas aproximaciones, que en
general se pueden encajar en alguna de las siguientes estrategias: Memoria compartida
y Paso de mensajes.
1.3.2. Cooperación entre procesos vía memoria compartida
La cooperación vía compartición son procesos que interaccionan con otros procesos
sin tener conocimiento explícito de ellos. Por ejemplo, múltiples procesos pueden
tener acceso a variables compartidas o a archivos o bases de datos compartidas. Los
procesos pueden usar y actualizar los datos compartidos sin referenciar otros procesos,
pero saben que otros procesos pueden tener acceso a los mismos datos. Así, los
procesos deben cooperar para asegurar que los datos que comparten son manipulados
adecuadamente. Los mecanismos de control deben asegurar la integridad (exactitud)
de los datos compartidos.
Dado que los datos están contenidos en recursos (dispositivos de almacenamiento,
memoria), los problemas de control de exclusión mutua, interbloqueo e inanición
están presentes de nuevo. La diferencia es que los datos individuales pueden ser
accedidos de dos maneras diferentes: lectura y escritura; y sólo las operaciones de
escritura deben ser mutuamente exclusivas.
Como ejemplo sencillo, considérese una aplicación en la que pueden ser actualizados
varios datos individuales. Supóngase que dos datos individuales a y b han de ser
mantenidos en la relación a = b. Esto es, cualquier programa que actualice un valor,
debe también actualizar el otro para mantener la relación. Considérense ahora los
siguientes dos procesos:
Si el estado es inicialmente consistente, cada proceso tomado por separado dejará los
datos compartidos en un estado consistente. Ahora considere la siguiente ejecución
concurrente en la cual los dos procesos respetan la exclusión mutua sobre cada celda
de memoria (a y b).
a = a + 1;
b = 2 * b;
b = b + 1;
a = 2 * a;
Al final de la ejecución de esta secuencia, la condición a = b ya no se mantiene. Por
ejemplo, si se comienza con a = b = 1, al final de la ejecución de esta secuencia,
tendremos a = 4 y b = 3. El problema puede ser evitado declarando en cada proceso la
secuencia completa como una sección crítica.
Así se ve que el concepto de sección crítica es importante en el caso de cooperación
por compartición.
1.3.3. Cooperación entre procesos vía comunicación
En los dos primeros casos, cada proceso tiene su propio entorno aislado que no
incluye a los otros procesos. Las interacciones entre procesos son indirectas. En el
caso de la competencia, hay recursos compartidos sin ser conscientes de los otros
procesos. En el segundo caso, hay compartición de valores y aunque cada proceso no
es explícitamente consciente de los demás procesos, es consciente de la necesidad de
mantener integridad de datos. Cuando los procesos cooperan vía comunicación, en
cambio, los diversos procesos involucrados participan en un esfuerzo común que los
vincula a todos ellos. La comunicación proporciona una manera de sincronizar o
coordinar actividades varias.
Típicamente, la comunicación se fundamenta en mensajes de algún tipo. Las
primitivas de envío y recepción de mensajes deben ser proporcionadas como parte del
lenguaje de programación o por el núcleo del sistema operativo.
Dado que en el acto de pasar mensajes los procesos no comparten nada, la exclusión
mutua no es un requisito de control en este tipo de cooperación. Sin embargo, los
problemas de interbloqueo e inanición están presentes. Como ejemplo de interbloqueo,
dos procesos pueden estar bloqueados, cada uno esperando por una comunicación del
otro.
1.4. Requisitos para implementar la exclusión mutua
La clave para evitar problemas en la sincronización de procesos (donde se comparte
memoria, archivos, periféricos, etc.) es impedir que dos o más procesos lean o
escriban los datos compartidos al mismo tiempo. En otras palabras, lo que se necesita
es exclusión mutua, es decir, algún mecanismo que asegure que, si un proceso está
utilizando algún elemento compartido, los demás procesos no pueden hacer lo mismo,
es decir, asegurar el acceso exclusivo de un proceso a un recurso.
Usualmente la mayor parte del tiempo, los procesos están ocupados realizando
cálculos internos y otras acciones donde no existe competencia. No obstante, hay
ocasiones en que el proceso tiene que acceder a recursos compartidos para realizar
tareas críticas que pueden generar competencia. La parte del programa en la que se
tiene acceso a estos recursos compartidos se denomina sección crítica o región crítica.
Si se pudieran organizar las cosas de modo que dos procesos nunca estuvieran en sus
regiones críticas al mismo tiempo, se evitaría la condición de carrera.
En la figura se muestra un ejemplo que podría darse. El Proceso A contiene varias
secciones críticas que se podrán usar para acceder a los mismos o a distintos tipos de
datos compartidos. De este modo, un proceso puede contener varias secciones críticas
para el mismo dato. Mientras que el Proceso B tiene sólo una sección crítica.
Proceso A Proceso B
Código NO crítico Código NO crítico
Sección crítica Sección crítica
Código NO crítico
Código NO crítico
Sección crítica
Código NO crítico
A continuación, se presenta un caso de uso de secciones críticas (marcado con línea
discontinua) en el sistema de reservaciones de una aerolínea.
Cualquier mecanismo o técnica que proporcione exclusión mutua debe cumplimentar
los siguientes requisitos:
1. Sólo se permite un proceso dentro de su sección crítica, de entre todos los
procesos que tienen secciones críticas para el mismo recurso u objeto
compartido.
2. No debe ser posible que un proceso que solicite acceso a una sección crítica sea
postergado indefinidamente.
3. Cuando ningún proceso esté en una sección crítica, a cualquier proceso que
solicite entrar en su sección crítica debe permitirsele entrar sin demora.
4. No se hacen suposiciones sobre las velocidades relativas de los procesos ni
sobre el número de procesadores.
5. Un proceso permanece dentro de su sección crítica sólo por un tiempo finito.
Hay varias maneras de satisfacer los requisitos para la exclusión mutua. Una manera
es a través de una implementación por hardware y otra es por software. A
continuación, se desarrollarán estos temas.
2. EXCLUSIÓN MUTUA
El control de la competencia involucra inevitablemente al SO ya que es quien asigna
los recursos. Además, los procesos necesitarán ser capaces por sí mismos de expresar
de alguna manera el requisito de exclusión mutua, bloqueando el recurso antes de
usarlo. Cualquier solución involucra algún apoyo del SO para proporcionar un servicio
de bloqueo o cerrojo.
La figura muestra el mecanismo de exclusión mutua en términos abstractos. Hay n
procesos para ser ejecutados concurrentemente. Cada proceso incluye una sección
crítica que opera sobre algún recurso Ra y código adicional que precede y sucede a la
sección crítica y que no involucra acceso a Ra. Dado que todos los procesos acceden
al mismo recurso Ra, se desea que sólo un proceso esté en su sección crítica al mismo
tiempo. Para aplicar exclusión mutua se proporcionan dos funciones: entrarcritica y
salircritica. Cada función toma como parámetro el nombre del recurso sujeto de la
competencia. Cualquier proceso que intente entrar en su sección crítica mientras otro
proceso está en su sección crítica, por el mismo recurso, deberá esperar.
2.1. Soporte Hardware: Deshabilitar interrupciones
En una máquina monoprocesador, los procesos concurrentes no pueden solaparse, sólo
pueden entrelazarse. Es más, un proceso continuará su ejecución hasta que invoque un
servicio del sistema operativo o hasta que sea interrumpido. Por tanto, para garantizar
la exclusión mutua, basta con impedir que un proceso sea interrumpido en su sección
crítica.
La exclusión mutua puede proporcionarse en forma de primitivas definidas por el
núcleo del sistema para deshabilitar y habilitar las interrupciones. Sin embargo, está
prácticamente en desuso a partir del advenimiento de los sistemas de
multiprocesamiento. Esto se debe a que el programa que corre en un procesador puede
deshabilitar las interrupciones de ese procesador, pero no de los otros, por lo tanto no
garantiza la exclusión mutua.
2.2. Soporte Hardware: Instrucciones hardware especiales
En una configuración multiprocesador, varios procesadores comparten acceso a una
memoria principal común. Como entre ellos hay una relación de igualdad, entonces,
no hay mecanismo de interrupción entre procesadores en que pueda basarse la
exclusión mutua.
A nivel de hardware, el acceso a una posición de memoria excluye cualquier otro
acceso a la misma posición. Con este fundamento, los diseñadores han propuesto
varias instrucciones hardware especiales que llevan a cabo dos acciones de manera
atómica (significa que la instrucción se realiza más de una acción en un único paso y
no puede ser interrumpida), como leer y escribir o leer y comprobar. Durante la
ejecución de la instrucción, el acceso a la posición de memoria se le bloquea a toda
otra instrucción que referencie esa posición.
Un ejemplo de instrucción hardware especial es TS (X) (Test-and-Set, lee y escribe), y
podría traducirse de la siguiente manera:
CRB1 5,X (Carga el registro base número 5 con valor de X y hace X = 1)
COMPR 5,0 (Compara el contenido del registro 5 con valor 0)
El funcionamiento sería cargar en el registro 5 el valor que tiene la variable
compartida cerrojo X y colocar en ese lugar un 1. Con esta instrucción hardware, ya
no importa lo que suceda después, pues si luego ocurre una interrupción, una de las
primeras cosas que se harían sería salvar los registros, y por ende el registro 5 en
particular, y además, ya también se cerró el cerrojo sin todavía saber cuál era el valor
del mismo.
Luego, lo que hace es comparar el registro 5 contra el valor 0. Si la variable
compartida valía 0, en el momento que se ejecutó la primera instrucción, ya vale 1;
entonces toma el recurso, ya que TS cerró el cerrojo.
Si el cerrojo valía 1, o sea que ya estaba cerrado, se le volvió a escribir un 1, o sea que
no cambió su estado. Y tampoco importa qué es lo que va a suceder después porque,
en realidad, se va a testear ese 1 en el registro 5 y éste indicará que el recurso está
ocupado y deberá esperar.
De esta manera se está asegurando que las interrupciones no afectarán el resultado de
ese fragmento de código.
Algunas ventajas del uso de instrucciones de hardware especiales son: fácil de
verificar y aplicable a cualquier número de procesos sobre cualquier número de
procesadores.
Algunas desventajas serían: uso de espera activa, posibilidad de inanición e
interbloqueo.
2.3. Soporte Software: Semáforos
2.3.1. Definición de semáforo
Como ya se ha explicado, a veces, los resultados dependen del orden de ejecución de
ciertos procesos, entonces debe garantizarse que no haya interferencia entre ellos,
garantizando el acceso exclusivo al recurso en competencia.
Quien debe proveer los mecanismos de comunicación y de sincronismo entre los
distintos procesos que compiten por el uso de los recursos es el Sistema Operativo.
Una herramienta muy difundida para llevar a cabo esta sincronización es el semáforo,
y se define como un conjunto de bits (uno o más) que revelan el estado de un
determinado dato, posición de memoria, recurso no compartible, etc. indicando si se
puede o no acceder al recurso en cuestión.
La información que brinda el estado del semáforo debe ser
respetada, de manera tal que, si indica en un determinado momento
que no se puede acceder al recurso, no lo haga (el recurso está
asignado a otro proceso).
Además, una vez que el proceso libera el recurso restringido, el SO
debe permitir el acceso de otro proceso al mismo. Este mecanismo
provee protección y sincronización entre procesos.
Para que pueda realizarse la sincronización a través de semáforos es
condición fundamental que los procesos utilicen una zona de almacenamiento
común, es decir, compartan la memoria de trabajo.
A continuación, se explicará el funcionamiento de los dos tipos de semáforos.
2.3.2. Semáforos Binarios
Como se dijo, los semáforos son variables comunes a varios procesos, que manifiestan
la posibilidad de acceder o no a un recurso.
Por cada recurso que necesite ser sincronizado existe un semáforo que indica el estado
de ese recurso, es decir, si está libre o está asignado a algún proceso.
Cuando un proceso desea acceder a un recurso, examina el valor del semáforo (se
identifica al semáforo como X) que está asociado al recurso. Si indica que el recurso
está libre (X = 0), entonces cierra el semáforo asignando 1 a X y toma el recurso.
Ahora bien, si al examinar X, éste tiene valor 1, el proceso debe esperar. La liberación,
apertura (X), consiste, sencillamente, en asignarle a X un valor igual a 0 y permitir
que otros procesos puedan usarlo.
Se establece la siguiente convención: si X es igual a 0, es de libre acceso; y si X es
igual a 1, significa que está ocupado.
Ahora bien, si un proceso necesita acceder a un recurso pero al consultar el estado del
semáforo éste le indica que está cerrado, esto es, que el recurso lo tiene asignado otro
proceso, debe pasar a un estado de espera (estado bloqueado). Entonces va a una cola
de espera del recurso (existe una cola de espera por cada recurso). Aparece aquí la
instrucción WAIT (X), que lo que hace es poner en una cola de espera asociada al
semáforo X al proceso que solicitó el acceso al recurso.
En el momento en que se hace la apertura (liberación) de un semáforo, habría que
examinar la cola asociada para saber si hay procesos que estén esperando la apertura
de ese semáforo. Para eso existe otra primitiva que se denomina SIGNAL (X), que
toma al primer proceso que se encuentra a la espera de ese recurso y lo envía a la cola
de listos. O sea que hace la función de despertar procesos.
Concluyendo, los semáforos binarios se gestionan a partir de cuatro acciones:
● CIERRE (X): tomar el recurso
● APERTURA (X): liberar el recurso
● WAIT (X): envía al proceso a la cola de espera de ese recurso
● SIGNAL (X): cuando se libera el recurso, despierta a un proceso de la cola de
espera
2.3.3. Semáforos Contadores
Edsger Dijkstra (informático y físico de origen holandés) ideó lo que se denomina
semáforos contadores, con el siguiente concepto: los semáforos pueden servir también
para contar la cantidad de procesos que hay en espera de un recurso.
Las operaciones básicas sobre un semáforo contador (X) son cinco:
● INIT (X): asigna un valor inicial al semáforo (usualmente 1, note la diferencia
con el semáforo binario que inicialmente está en cero)
● CIERRE (X), APERTURA (X), WAIT (X) y SIGNAL (X) son similares a los
semáforos binarios
El algoritmo de cierre del semáforo es bastante similar al del semáforo binario.
Cuando un proceso quiere acceder a un recurso consulta el estado del semáforo
decrementando en 1 su valor (recuerde que su valor inicial es 1). El único caso en que
se puede tomar el recurso, es si este decremento causó que X = 0, lo cual quiere decir
que estaba en 1, que es el valor de acceso permitido. En cambio, si da X < 0, el
semáforo estaba al menos en 0, es que decir que un proceso tiene tomado el recurso (y
podrían haber otros procesos esperando, lo cual lo da el valor absoluto del semáforo).
Por ejemplo, si el valor del semáforo X es -3, significa que el recurso asociado a ese
semáforo está asignado a un proceso y que, además, hay otros 3 (valor absoluto)
procesos esperando por ese recurso. Esta información es valiosa para la administración
del sistema ya que permite detectar recursos muy demandados y, si lo permite el tipo
de recurso, su balanceo hacia otros recursos similares poco utilizados. Por ejemplo, si
se envían documentos a imprimir a la impresora por defecto del sistema y hay más de
una impresora en el sistema, podría ocurrir que la impresora por defecto esté
sobrecargada y las demás no. Con semáforos contadores se podría detectar esta
situación y redirigir a hacia otras impresoras los documentos.
La operación de Apertura (X), correspondiente a la liberación del recurso, implica la
necesidad de incrementar el valor de X. Si X es menor a 0 es que hay procesos
esperando. Entonces, se despierta al primero y se lo pone en la cola de LISTOS.
Las acciones de Apertura y Cierre se realizan atómicamente, es decir, no pueden ser
interrumpidas.
En la figura se observa que la Impresora 2 (el recurso) está asignada al Proceso 11 y,
por ende, el semáforo indica que el recurso está asignado (color rojo). Además, el
semáforo contador indica que hay 3 procesos (Procesos 4, 32 y 16) en cola de espera
para utilizar ese recurso.
2.3.4. El problema del barbero dormilón
El problema del barbero dormilón es un problema clásico de sincronización. En él se
propone la discusión sobre cómo gestionar el “tránsito” por una pequeña peluquería
entre el barbero y los clientes. El
enunciado sugiere que durante la
ejecución la interacción entre el barbero
y los clientes se producen muy a menudo
y que, por tanto, deben establecerse los
mecanismos de sincronización adecuados
para evitar que los dos colisionen dentro
la barbería; es decir, asegurar que uno
sólo es el que se mueve en cada
momento.
El problema consiste en una barbería en
la que trabaja un barbero que tiene un
único sillón de barbero (recurso dedicado
por el que compiten los clientes) y varias
sillas para esperar (en la imagen hay 5 sillas). Cuando no hay clientes, el barbero se
sienta en una silla y se duerme.
Cuando entra a la barbería un nuevo cliente, éste o bien despierta al barbero o, si el
barbero está afeitando a otro cliente, se sienta en una silla (o espera fuera de la
barbería si todas las sillas están ocupadas por clientes esperando). El problema
consiste en realizar la actividad del barbero sin que ocurran condiciones de carrera. La
solución implica el uso de semáforos y objetos de exclusión mutua para proteger la
sección crítica.
En este problema se interpreta al barbero y a los clientes como procesos; y a la silla
del barbero y demás sillas como objetos a aplicar la exclusión mutua. En el cuadro se
observa un ejemplo de una posible definición de semáforos del sistema:
Semáforo Wait Signal
Sillas El cliente espera una silla vacía El cliente que abandona la silla avisa a un
cliente que espera silla
Silla_barbero El cliente espera hasta que la silla del El barbero avisa cuando su silla queda vacía
barbero esté vacía
Cliente_listo El barbero espera hasta que el cliente esté en El cliente avisa al barbero que ya está en la
la silla silla
Terminado El cliente espera a que el corte esté completo El barbero avisa cuando termina el corte a
un cliente
El siguiente es el mismo concepto, pero aplicado a tres barberos atendiendo una
barbería.
2.4. Soporte Software: Monitores
El problema del uso de los semáforos es que las llamadas a las funciones necesarias
para utilizarlos quedan repartidas en el código del programa, haciendo difícil corregir
errores y asegurar el buen funcionamiento de los algoritmos. Para evitar estos
inconvenientes se desarrollaron los monitores.
Como definición de monitor se dirá que es un programa de servicio que se encarga
de realizar la tarea de exclusividad y sincronización. Esto lo logra al permitir que los
procesos puedan llamar al monitor cada vez que lo deseen, pero no pueden interactuar
directamente con el recurso en competencia sino por intermedio del monitor y es éste
quien lo administra otorgando la exclusividad y sincronización del recurso.
Un monitor es un módulo de software consistente en:
● Módulo de Inicialización: contiene el código a ser ejecutado cuando el monitor
es creado.
● Procedimientos del monitor: son los procedimientos que pueden ser llamados
desde fuera del monitor.
● Datos locales o privados: que sólo son accesibles por los procedimientos del
monitor y no por ningún procedimiento externo.
Los monitores están pensados para ser usados en entornos multiproceso o multihilo, y
por lo tanto muchos procesos o threads pueden llamar a la vez a un procedimiento del
monitor. Los monitores garantizan que, en cualquier momento, a lo sumo una hebra
se pueda estar ejecutando dentro de un monitor.
Ejecutar dentro de un monitor significa que sólo un hilo está en estado Activo
mientras dura la llamada a un procedimiento del monitor. El problema de que dos
threads ejecuten un mismo procedimiento dentro del monitor es que se pueden dar
condiciones de carrera, perjudicando el resultado de los cálculos. Para evitar esto y
garantizar la integridad de los datos privados, el monitor hace cumplir la exclusión
mutua implícitamente, de modo que sólo un procedimiento esté siendo ejecutado por
vez. De esta forma, si un hilo llama a un procedimiento mientras otro hilo está dentro
del monitor, se bloqueará y esperará en la cola de entrada hasta que el monitor quede
nuevamente libre.
Un monitor (en la jerga llamado demonio) es un software que permanece residente en
la memoria RAM del sistema para que pueda ser invocado por cualquier proceso.
2.5. Soporte Software: Paso de mensajes
Para que los semáforos y los monitores funcionen, es necesario que los procesos o
hilos se ejecuten en procesadores que comparten el acceso a una misma memoria.
Ahora bien, si esta situación no se da (en sistemas débilmente acoplados), entonces
este enfoque no resulta aplicable.
El paso de mensajes tiene la ventaja añadida de que puede ser implementado tanto en
sistemas distribuidos como en multiprocesadores de memoria compartida y sistemas
monoprocesador.
La funcionalidad real del paso de mensajes se proporciona normalmente en forma de
un par de instrucciones primitivas:
● Send (destino, mensaje)
● Receive (origen, mensaje)
Este es el conjunto mínimo de operaciones necesarias para que los procesos puedan
entablar la comunicación a través de mensajes. El proceso envía información en forma
de un mensaje a otro proceso designado como destino a través de un identificador (id)
que lo distingue entre todos los procesos del sistema.
La comunicación de un mensaje entre dos procesos implica cierto nivel de
sincronización entre los dos y también que se conocen explícitamente (conoce el id del
destino).
Cuando una primitiva send se ejecuta en un proceso, hay dos posibilidades: el proceso
que envía se bloquea hasta que el mensaje se recibe o no se bloquea. De igual modo,
cuando un proceso recibe la primitiva receive.
Así, ambos emisor y receptor pueden ser bloqueantes o no bloqueantes. Hay varias
combinaciones típicas, si bien un sistema concreto puede normalmente implementar
sólo una de varias combinaciones.
3. BIBLIOGRAFÍA
Fundamentos de Sistemas Operativos, A. Silberschatz
Sistemas Operativos. Aspectos internos y principios de funcionamiento, W. Stallings
Sistemas operativos modernos. A. Tanenbaum
Sistemas operativos. Un enfoque basado en conceptos. D. Dhamdhere