Sistemas operativos modernos
procedimientos insertarjelem y sacar^elem, que no se muestran, se encargan de los pormeno res de colocar elementos en el búfer y sacarlos de él. Ahora volvamos a la condición de competencia. Puede presentarse porque el acceso a cuenta no está restringido. Podría darse la siguiente situación: el búfer está vacío y el consu midor acaba de leer cuenta para ver si es 0. En ese instante, el calendarizador decide dejar de ejecutar el consumidor por el momento y comienza a ejecutar el productor. Éste inserta un ele mento en el búfer, incrementa cuenta y observa que ahora tiene el valor 1. Eso implica que an tes cuenta era O, así que el consumidor debe estar inactivo; por lo tanto, el productor llama a wakeup para activarlo. Por desgracia, éste todavía no está inactivo lógicamente, por lo que la señal de activar se pierde. La siguiente vez que se ejecute el consumidor, probará el valor de cuenta que ya había leído, verá que es O, y se desactivará. Tarde o temprano el productor llenará el búfer hasta el tope y se desactivará. Ambos estarán inactivos por siempre. La esencia del problema es que se pierde una señal de activar enviada a un proceso que no está inactivo (todavía). Si no se perdiera, todo funcionaría. Una solución rápida es modificar las reglas y añadir un bit de espera para activar. Cuando se intenta activar un proceso que está acti vo, se establece este bit. Después, cuando el proceso quiera desactivarse, si el bit de espera está encendido, se apagará, pero el proceso seguirá activo. El bit de espera para activar es una espe cie de alcancía para señales de activar. Aunque el bit de espera para activar nos salva en este sencillo ejemplo, es fácil idear ejem plos con tres o más procesos en los que un bit de espera no basta. Podríamos añadir un segun do bit de espera, o quizá ocho o 32, pero en principio el problema persiste. 2.3.5 Semáforos Ésta era la situación en 1965, cuando E. W. Dijkstra (1965) sugirió el uso de una variable en tera para contar el número de llamadas wakeup guardadas para uso futuro. En su propuesta introdujo un nuevo tipo de variable llamada semáforo. Un semáforo puede tener el valor O, que indica que no se guardaron llamadas wakeup, o algún valor positivo si hay llamadas pen dientes. Dijkstra propuso tener dos operaciones: down y up (generalizaciones de sieep y wakeup, respectivamente). La operación down aplicada a un semáforo determina si su valor es mayor que 0. En tal caso, decrementa el valor (es decir, gasta un despertar almacenado) y simplemen te continúa. Si el valor es O, el proceso se desactiva sin terminar la operación down por el mo mento. Verificar el valor, modificarlo y desactivarse (en su caso) se efectúan como una sola acción atómica (es decir, indivisible). Se garantiza que una vez iniciada una operación de se máforo, ningún otro proceso podrá tener acceso al semáforo antes de que la operación haya ter minado o se haya bloqueado. Esta atomicidad es indispensable para resolver problemas de sincronización y evitar condiciones de competencia. La operación up incrementa el valor del semáforo indicado. Si uno o más procesos esta ban inactivos en espera de ese semáforo, sin haber podido terminar una operación down pre via, el sistema escoge uno de ellos (quizá al azar) y le permite terminar dicha operación. Así,
RkJQdWJsaXNoZXIy MjI4NDcx