Las condiciones de competencia
<keywords content="TTL 74Ls164N, electronica, circuito, pic, NE555, PIC BASIC, PIC SIMULATOR IDE, esquema, circuito impreso, proyecto, gratis, download, programa, CMOS, pin, e/s, i/o, ucontrol, PIC, 16F628a, 16f84a" />
Como introduje en los posts anteriores, las condiciones de competencia son uno de los problemas potenciales que se presentan en la programación con Sistemas Operativos, pero: ¿Qué es una condición de competencia?
Básicamente es una bronca entre procesos que termina con el cuelgue de nuestro sistema, pero esa no es una definición apropiada para este problema, una mas acertada sería esta: una condición de competencia es un fenómeno que se produce cuando uno o varios procesos compiten por uno o varios recursos produciendo el bloqueo en la ejecución de esos procesos, lo que a la postre termina con el cuelgue del sistema.
Entonces una condición de competencia es una de las pesadillas que un programador de aplicaciones que utiliza algún Sistema Operativo debe evitar si desea dormir tranquilamente. En mi post anterior puse un ejemplo en el cual se produce una condición de competencia, si se analiza con más detenimiento podemos darnos cuenta que es muy difícil definir bajo que condiciones se producirá una condición de competencia; sin embargo, como este es un tema investigado desde los años 60, un grupo de gente ya se ha ocupado de identificar bajo que condiciones un programa será robusto ante condiciones de competencia.
Para evitar una condición de competencia debemos establecer un conjunto de reglas que nos aseguren que no se producirá, adicionalmente, el Sistema Operativo debe ofrecer al programador un conjunto de herramientas de programación que le permitan poner en práctica estas reglas.
De hecho, a lo largo del curso en los ejemplos que he puesto, hemos estado evitando las condiciones de competencia, sólo que no se los dije para no complicar las cosas más de lo que ya eran en aquel entonces, pero es el momento de tocar este tema y aquí vamos.
La regla de oro para evitar las condiciones de competencia es garantizar el acceso seguro a recursos compartidos, es decir, debemos hacer coordinación y sincronización de procesos.
Para lograr implementar esta regla es que se inventaron las secciones críticas, pero el utilizar las herramientas de implementación de secciones críticas no asegura evitar una condición de competencia, ese es el caso del ejemplo de mi mensaje anterior.
Bien, si aún utilizando secciones críticas no se asegura evitar que se produzca una condición de competencia, ¿que podemos hacer para resolver el problema? Lo que podemos hacer es poner restricciones en la semántica de nuestro código que aseguren que el código es seguro, valga la redundancia.
El párrafo anterior nos pone otra vez en aprietos porque debemos preguntarnos ¿entonces cuales son esas restricciones semánticas? La respuesta a esa pregunta está dada en las siguientes cuatro reglas que debemos cumplir:
* Dos o más procesos no pueden estar simultáneamente dentro de sus regiones críticas * No debe suponerse nada acerca de la velocidad o el orden de ejecución de los procesos * Ningún proceso que se ejecute fuera de su región crítica puede bloquear a otros procesos * Ningún proceso deberá tener que esperar indefinidamente para entrar a su región crítica
Como veremos en el futuro, hacer que nuestros programas cumplan con estas restricciones puede ser realmente doloroso, (al menos para nuestra cabeza), pero también veremos que ya algunos se rompieron la cabeza por nosotros y nos ofrecen soluciones tipo para la mayoría de los problemas a los que debemos enfrentarnos, estas soluciones se presentan con ejemplos bastante didácticos que perecen tonterías pero que podemos trasladar a problemas reales que son muy semejantes en su dinámica.
Ahora analicemos detalladamente cada una de las reglas semánticas que debe cumplir un programa libre de condiciones de competencia:
Dos o más procesos no pueden estar simultáneamente dentro de sus regiones críticas
Cuando introduje las secciones críticas expliqué que su misión fundamental era evitar que más de un proceso accediera a un recurso compartido, esto implica que para ello debemos contar con algún mecanismo de protección y este mecanismo resultó ser el uso de semáforos, pero los semáforos por si solos no garantizan la ejecución segura, debemos pensar donde ponerlos y cuantos necesitamos para evitar que más de un proceso se meta en el código que manipula al recurso compartido.
En ocasiones se permite que más de un proceso acceda al recurso compartido para realizar lecturas pero cuando se hagan escrituras debemos impedir que más de un proceso esté dentro de la sección crítica, esta es una variación a la regla tal como la hemos definido, pero esta situación se da en muy contadas ocasiones así que no debemos preocuparnos mucho por esta variación.
No debe suponerse nada acerca de la velocidad o el orden de ejecución de los procesos
Este es un tema que podemos subestimar porque tradicionalmente programamos pensando que tenemos para nosotros todo el equipo de cómputo, pero cuando utilizamos Sistemas Operativos las condiciones cambian mucho porque cada proceso actúa como si todo el equipo de cómputo fuese suyo en el momento en que está ejecutándose, por lo que debemos proteger los recursos compartidos adecuadamente ante los cambios de contexto de los procesos.
Para ello existen, por supuesto, las secciones críticas; pero esta nueva regla le indica al programador que no puede soportar la semántica de su programa en suposiciones de carácter temporal o de orden de ejecución de los procesos y eso quedó claro en uno de los primeros ejemplos que puse en este cursillo.
Ningún proceso que se ejecute fuera de su región crítica puede bloquear a otros procesos
Esta regla lo que define es que el bloqueo a otros procesos debe realizarse justo antes de entrar a la sección crítica, para evitar que otro proceso sea detenido cuando en realidad puede ejecutar código de forma segura, igualmente los procesos que hayan sido bloqueados cuando se entró en la sección crítica deben ser desbloquedaos al salir de esta.
Además al poner en práctica esta regla reducimos al máximo el tamaño del código dentro de la sección crítica y con ello maximizamos el uso de la CPU y minimizamos los tiempos de bloqueo, así que aunque parece simple de poner en práctica, veremos que no es tan simple lograr estos objetivos.
Ningún proceso deberá tener que esperar indefinidamente para entrar a su región crítica
Ahora se trata de evitar que un proceso muera de inanición, es decir, que estando listo para ejecutarse y requiriendo que se ejecute, otro proceso mucho más rápido o prioritario bloquee a uno más lento, menos prioritario o ambas cosas a la vez, matándolo de hambre. Este problema puede ser muy solapado y por ello muy peligroso.
Normalmente los sistemas de planificación de procesos de los Sistemas Operativos implementan mecanismos que tratan de evitar el fenómeno de inanición, pero estos mecanismos no son infalibles y los programadores debemos estar pendientes de lo que hacemos para evitarlo.
Hasta aquí el post de hoy porque ya tengo dolor de cabeza y creo que es suficiente materia para provocarle el mismo problema a alguno de ustedes. En la próxima entrega comenzamos a ver problemas y programas para evitar las condiciones de competencia así que preparen los compiladores y el coco, tengan agua o hielo cerca para refrescarlo y no se asusten que esto es difícil pero no imposible.
Autor
| |
Datos del Autor | |
| Nombre: | Reinier Torres Labrada | |
| email: | reinier@ucontrol.com.ar | |
| Ver los artículos de este autor. Página con el perfil del autor. | ||