You are on page 1of 9

CAPTULO 7.

PROGRAMACIN CONCURRENTE
7 Programacin concurrente. ............................................................................................ 2 7.1 La nocin de proceso ............................................................................................. 2 7.1.1 Construcciones de programacin concurrente ............................................... 4 7.2 Ejecucin concurrente............................................................................................ 4 7.2.1 Procesos y objetos.......................................................................................... 6 7.3 Representacin de procesos ................................................................................... 7 7.3.1 Corrutinas....................................................................................................... 7 7.3.2 Fork y join...................................................................................................... 7 7.3.3 Cobegin .......................................................................................................... 8 7.3.4 Declaracin explicita de procesos.................................................................. 8

Rafael lvarez Garca ltima revisin 29-10-07 rafpalvarez@gmail.com Nota importante: Este documento no pretende reemplazar al material propuesto por la UNED para la asignatura Sistemas en Tiempo Real. Cualquier sugerenca, comentario o correccin sobre este documento, envelo a rafpalvarez@gmail.com para poder realizar los cambios necesarios.
1

7 Programacin concurrente.
Virtualmente, todos los sistemas de tiempo real son inherentemente concientes. Los lenguajes destinados a ser usados en este dominio tienen mayor potencia expresiva si proporcionan al programador primitivas que se ajusten al paralelismo de las aplicaciones. Denominamos programacin concurrente a la notacin y tcnicas de programacin que expresan el paralelismo potencia] y que resuelven los problemas resultantes de la sincronizacin y la comunicacin. La implementacin del paralelismo es un tena de los sistemas informticos (hardware y software) esencialmente independiente de la programacin concurrente. La importancia de la programacin concurrente esta en que proporciona un entorno abstracto donde estudiar el paralelismo sin tener que enfrascarse en los detalles de implementacin, (Ben-Ari, 1982)

7.1 La nocin de proceso


Cualquier lenguaje, natural o informtico, tiene un carcter dual: a la vez que es capaz de expresarse, tambin limita el entorno en el que se aplica dicha capacidad expresiva. Si un lenguaje no permite cierta nocin o concepto particular, entonces aquellos que utilizan el lenguaje no podrn emplear este concepto, y puede que, incluso, desconozcan por completa su existencia. Pascal, C, FORTRAN y COBOL comparten la propiedad comn de ser lenguajes de programacin secuenciales. Los programas escritos en estos lenguajes tienen un nico hilo de control. Comienzan la ejecucin en cierto estado y avanzan ejecutando una sentencia cada vez, hasta que el programa finaliza. La traza a travs del programa puede diferir debido a variaciones en los datos de entrada, aunque para una ejecucin concreta del programa existe una nica traza. Esto no es lo adecuado para la programacin de sistemas de tiempo real. Un programa concurrente puede verse como un conjunto de procesos secuenciales autnomos, que son ejecutados en paralelo. Todos los lenguajes de programacin concurrente incorporan, explicita o implcitamente, la nocin de proceso; cada. proceso tiene un hilo de control. La implementacin real (esto es, la ejecucin) de un conjunto de procesos tiene lugar normalmente de tres formas. Los procesos pueden; (1) Multiplexar sus ejecuciones sobre un nico procesador. (2) Multiplexar su ejecuciones en un sistema multiprocesador con acceso a memoria compartida. (3) Multiplexar sus ejecuciones en diversos procesadores que no comparten memoria (a estos sistemas se les denomina, normalmente, sistemas distribuidos). Tambin es posible encontrar hbridos de estos tres mtodos. Solo en los casos (2) y (3) es posible una verdadera ejecucin paralela de mas de un proceso. El termino concurrente indica paralelismo potencial. Los lenguajes de programacin concurrente permiten al programador expresar actividades lgicamente paralelas sin tener en cuenta su implementacin. Considerando as los procesos, queda claro que la ejecucin de un programa concurrente no es tan directa como la ejecucin de un programa secuencial. Los procesos deben ser creados y finalizados, as 2

como distribuidos hacia / desde los procesadores disponibles. Estas actividades son efectuadas por el sistema de soporte de ejecucin (RTSS; Run-Time Support System) o ncleo de ejecucin. El RTSS posee muchas de las caractersticas del planificador de un sistema operativo, y esta ubicado, lgicamente, entre el hardware y el software de aplicacin. En realidad, puede tomar una de las siguientes formas: (1) Una estructura software programada como parte de la aplicaci6n (esto es, como un componente del programa concurrente). Esta es la aproximacin adoptada por el lenguaje Modula-2. (2) Un sistema software estndar generado junto al cdigo objeto del programa por el compilador. Esta es la estructura habitual en los programas de Ada y Java. (3) Una estructura hardware micro codificada en el procesador, por motivos de eficiencia. Un programa occam2 que se ejecuta en un transputer tiene este sistema de ejecucin. El algoritmo que utiliza el RTSS para planificar (esto es, para decidir que proceso se ejecuta a continuacin en el caso de haber mas de uno ejecutable) afectara al comportamiento temporal del programa, aunque, para programas bien construidos, el comportamiento lgico no depender del RTSS. Desde el punto de vista del programa, se asume que el RTSS planifica los procesos de forma no determinista. En el caso de los sistemas de tiempo real las caractersticas de la planificacin son relevantes. Todos los sistemas operativos proporcionan mecanismos para crear procesos concurrentes. Normalmente, cada proceso se ejecuta en su propia maquina virtual, para evitar interferencias con otros procesos no relacionados. Cada proceso consta, realmente, de un nico programa. Sin embargo, en los ltimos aos se tiende a permitir la creacin de procesos dentro de los programas. Los sistemas operativos modernos permiten crear procesos dentro del mismo programa accediendo de modo compartido, y sin restricciones, a la memoria comn (estos procesos suelen llamarse hilos o hebras). Por tanto. en los sistemas operativos que se ajustan a POS1X. es preciso distinguir entre la concurrencia de programas (procesos), y la concurrencia dentro de un programa (hilos). tambin es frecuente distinguir entre aquellos hilos visibles desde el sistema operativo y aquellos que provienen nicamente del soporte de ciertas rutinas de biblioteca. Ha habido un amplio debate entre programadores, diseadores de lenguajes y diseadores de sistemas operativos, sobre si lo apropiado es que sea el lenguaje quien de soporte para la concurrencia, o si ste debiera ser proporcionado nicamente por el sistema operativo. Los argumentos a favor de incluir la concurrencia en los lenguajes de programacin son los siguientes: (1) Lleva a programas mas legibles y fciles de mantener. (2) Existen muchos tipos distintos de sistemas operativos; al definir la concurrencia en el lenguaje se consiguen programas mas portables. (3) Puede que el computador embebido ni siquiera disponga de un sistema operativo residente. Claramente, estos fueron los argumentos que mas pesaron sobre los diseadores de Ada y Java. Los argumentos en contra de la concurrencia en el lenguaje son los siguientes: (1) Cada lenguaje tiene un modelo de concurrencia distinto; resulta mas sencillo componer programas de distintos lenguajes si todos utilizan el mismo modelo de concurrencia del sistema operativo. (2) Puede no ser fcil implementar eficientemente cierto modelo de concurrencia de un lenguaje sobre algn modelo de sistema operativo. (3) Comienzan a aparecer estndares de sistema operativo, y por tanto los programas se vuelven mas portables. 3

La necesidad de soportar diversos lenguajes fue una de las principales razones por las que la industria aeronutica civil, al desarrollar su programa de Avionica Modular Integrada, opto por una interfaz de programacin de aplicaciones ncleo estndar (llamada APEX) que soporta concurrencia, en vez de adoptar el modelo de concurrencia de Ada (ARINC AEE Committee, 1999). El debate, sin duda, continuara durante algn tiempo.

7.1.1 Construcciones de programacin concurrente


A pesar de que las construcciones de programacin concurrente varan de un lenguaje (y sistema operativo) a otro, deben proporcionar tres servicios fundamentales, que se citan a continuacin; La expresin de ejecucin concurrente mediante la nocin de proceso. La sincronizacin de procesos. La comunicacin entre procesos.

Al considerar la interaccin entre procesos, es til distinguir entre tres tipos de comportamiento: Independiente Cooperativo Competitivo

Los procesos independientes no se comunican o sincronizan entre si. Por contra, los procesos cooperativas se comunican con regularidad y sincronizan sus actividades para realizar alguna operacin comn. Un sistema informtico consta de un nmero finito de recursos que pueden ser compartidos entre los procesos; por ejemplo, perifricos, memoria y potencia de procesador. Para que los procesos obtengan su proporcin justa de recursos, deben compear entre si. La asignacin de recursos necesita inevitablemente comunicacin y sincronizacin entre los procesos del sistema. Pero, aunque estos procesos se comuniquen y sincronicen para obtener recursos, son esencialmente independientes.

7.2 Ejecucin concurrente.


A pesar de que la nocin de proceso es comn a todos los lenguajes de programacin concurrente, hay variaciones considerables en los modelos de concurrencia que se adoptan. Estas variaciones conciernen a los siguientes elementos: Estructura Nivel Granularidad Inicializacin Finalizacin Representacin.

La estructura de un proceso puede ser clasificada de la siguiente forma:

Esttica: el nmero de procesos es fijo y conocido en tiempo de compilacin. Dinmica: los procesos son creados en cualquier momento. El nmero de procesos existentes solo se determina en tiempo de ejecucin. 4

Otra distincin entre lenguajes proviene del nivel de paralelismo soportado. De nuevo, se pueden identificar dos casos distintos: Anidado: los procesos se definen en cualquier nivel del texto del programa; en particular, se permite definir procesos dentro de otros procesos. Piano: los procesos se definen nicamente en el nivel mas externo del texto del programa.

Dentro de los lenguajes que permiten construcciones anidadas, existe tambin una interesante distincin entre lo que se puede llamar paralelismo de grano grueso y de grano fino. Un programa concurrente de grano grueso contiene relativamente pocos procesos, cada uno con una historia de vida significativa. Por su parte, los programas con paralelismo de grano fino tienen un numero mayor de procesos sencillos, algunos de los cuales existen para una nica accin. La mayora de los lenguajes de programacin concurrentes, representados por Ada, muestran paralelismo de grano grueso. Occam2 es un buen ejemplo de lenguaje concurrente con paralelismo de grano fino. Cuando se crea un proceso, puede ser necesario proporcionar informacin relacionada con su ejecucin (de la misma forma que un subprograma requiere cierta informacin cuando es invocado). Hay dos formas de realizar esta inicializacin: la primera es pasar al proceso la informacin en forma de parmetros; la segunda es la comunicacin explicita con el proceso, despus de que haya comenzado su ejecucin. La finalizacin de procesos se puede realizar de distintas formas. A continuacin se resumen las circunstancias en las que se permite que un proceso finalice: (1) Finalizacin de la ejecucin del cuerpo del proceso. (2) Suicidio por ejecucin de una sentencia de auto finalizacin. (3) Aborto por medio de una accin explcita de otro proceso. (4) Ocurrencia de una condicin de error sin tratar. (5) Nunca; procesos que se ejecutan en bucles que no terminan. (6) Cuando ya no son necesarios. Con la anidacin en niveles, es posible crear jerarquas de procesos y establecer relaciones entre ellos. Para cualquier proceso, es til distinguir entre el proceso (o bloque) que es responsable de su creacin, y el proceso (o bloque) que es afectado por su finalizacin. La primera relacin es conocida como padre / hijo, y posee la caracterstica de que el padre puede ser detenido mientras et hijo se crea e inicializa. La segunda se denominada guardian/dependiente, y en el la un proceso puede depender del propio proceso guardin o de un bloque interno de este. El guardin no puede terminar un bloque hasta que todos los procesos dependientes hayan terminado (esto es, un proceso no puede existir fuera de su alcance). Consecuentemente, un guardin no puede terminar hasta que lo hayan hecho todos sus procesos dependientes. La consecuencia de esta regla es que un programa no podr terminar hasta que todos los procesos creados en el hayan terminado tambin.

En algunas situaciones, el padre de un proceso Ser tambin su guardin. Este ser el caso cuando se utilicen lenguajes que solo permiten estructuras estticas de procesos (por ejemplo occam2). Con estructuras dinmicas de procesos (que tambin son anidadas), el padre y el guardin pueden no ser el mismo. Esto se ilustrara posteriormente cuando se vea Ada. Una de las formas en que un proceso puede finalizar punto (3) de la lista anterior es por la aplicacin de una sentencia que lo aborta (abort). La existencia de abort en los lenguajes de programacin concurrentes es una cuestin de cierta controversia. Para una jerarqua de procesos suele ser necesario que la interrupcin de un guardin implique la interrupcin de todos los dependientes (y de los dependientes de estos, y as recursivamente). La ultima circunstancia de finalizacin de la lista anterior se ver con mas detalle al describir los mtodos de comunicacin de procesos. En esencia, permite que un proceso servidor termine si el resto de procesos que podran comunicar con el ya han terminado.

7.2.1 Procesos y objetos


El paradigma de programacin orientada al objeto anima a que los constructores de sistemas (y de programas) consideren el artefacto en construccin como un conjunto de objetos cooperantes o, para utilizar un termino mas neutral, de entidades. Bajo este paradigma, es provechoso considerar dos tipos de objetos: activos y reactivos. Los objetos activos acometen acciones espontneamente (con un procesador de por medio): hacen posible que la computacin prosiga. Los objetos reactivos, por contra, solo entran en accin cuando son invocados por un objeto activo. Otros paradigmas de programacin, como el de flujos de datos o el de redes de tiempo real, identifican agentes activos y datos pasivos. Solo las entidades activas dan lugar a acciones espontneas. Los recursos son reactivos, aunque pueden controlar el acceso a su estado interno (y a cualquier recurso real que controlen). Algunos recursos solo pueden ser usados por un nico agente en cada momento; en otros casos, las operaciones realizables en un determinado momento dependen de los estados actuales de los recursos. Un ejemplo comn del ultimo caso es el del buffer de datos, cuyos elementos no pueden ser extrados si esta vaco. El termino pasivo se utilizara para designar entidades reactivas que permiten un acceso completo. La implementacin de entidades de recurso requiere algn tipo de agente de control. Si el agente de control es pasivo (como un semforo), entonces se dice que el recurso esta protegido (o sincronizado). Por otro lado, si se precisa de un agente activo para programar el nivel adecuado de control, podemos decir, en cierto sentido, que el recurso es activo. El termino servidor se utilizara para identificar a este ultimo tipo de entidades, y el termino recurso protegido para el tipo pasivo. En un lenguaje de programacin concurrente, las entidades activas se representan mediante procesos. Las entidades pasivas pueden representarse directamente como variables de datos, o pueden ser encapsuladas por alguna construccin mdduio/paquete/clase que proporcione una interfaz procedural. Los recursos protegidos tambin pueden estar encapsulados en una construccin tipo modulo, y necesitar disponer de un servicio de sincronizacin de bajo nivel. Los servidores requieren un proceso, ya que es necesario programar el agente de control. Una cuestin clave para los diseadores de lenguajes es soportar o no primitivas para los recursos protegidos y para los servidores. Los recursos suelen implementarse eficientemente (al menos en sistemas uniprocesadores), ya que normalmente emplean un agente de control de bajo nivel (por 6

ejemplo un semforo). Pero, para algunas clases de programas, esto puede ser poco flexible, y llevar a estructuras de programa deficientes (esto se discute con mas detalle en el Capitulo 8). Los servidores son esencialmente flexibles, ya que el agente de control se programa mediante un proceso. El inconveniente de esta aproximacin es que puede desembocar en un aumento de procesos y, en consecuencia, en un elevado numero de cambios de contexto durante la ejecucin. Esto es particularmente problemtico si el lenguaje no permite recursos protegidos y hay que utilizar un servidor para cada cantidad.

7.3 Representacin de procesos


Hay tres mecanismos bsicos para representar la ejecucin concurrente; fork y join, cobegin y la declaracin explicita de procesos. A veces se incluyen tambin las corrutinas como mecanismo para expresar la ejecucin concurrente.

7.3.1 Corrutinas
Las corrutinas son como subrutinas, salvo que permiten el paso explicito de control entre ellas de una forma simtrica en vez de estrictamente jerrquica. El control se transfiere de una corrutina a otra mediante una sentencia reanuda (resume) que incluye el nombre de la corrutina con la que se continua. Cuando una corrutina realiza una reanudacin, deja de ejecutarse, pero guarda informacin del estado local, de forma que si, posteriormente, otra corrutina la hace reanudar, podr retomar su ejecucin. Cada corrutina puede verse como parte de un proceso separado; sin embargo, no es necesario un sistema de soporte de ejecucin, ya que las propias corrutinas se colocan en orden de ejecucin. Claramente, las corrutinas no son adecuadas para un autentico procesamiento paralelo, ya que su semntica solo permite la ejecucin de una rutina cada vez. Modula-2 es un ejemplo de lenguaje que soporta corrutinas.

7.3.2 Fork y join


Esta sencilla aproximacin no proporciona una entidad visible de proceso, sino que aporta dos instrucciones. La instruccin fork (bifurca) indica que cierta rutina deber comenzar a ejecutarse concurrentemente con quien ha invocado fork. La instruccin join (rene) permite al que invoca detenerse, y por ende sincronizarse, hasta la terminacin de la rutina invocada. Por ejemplo:

El lenguaje Mesa proporciona la notacin fork y join. tambin POSIX proporciona una versin de fork y join; aunque aqu fork sirve para crear una copia del invocador, y el join efectivo se obtiene mediante la llamada del sistema wait. Fork y join permiten crear procesos dinmicamente, y proporcionan un mecanismo para pasar informacin al proceso hijo a travs de parmetros. Normalmente, al terminar el hijo, devuelve un solo valor. Aunque flexibles, fork y join no proporcionan una aproximacin estructurada a la creacin de procesos, y su utilizacin es propensa a errores. Por ejemplo, un proceso guardin debe reunir explcitamente todos los procesos dependientes, en vez de esperar simplemente a que terminen.

7.3.3 Cobegin
Cobegin (o par) es una forma estructurada de denotar la ejecucin concurrente de un conjunto de instrucciones: Cobegin S1; S2; Sn; coend Este cdigo permite la ejecucin concurrente de las instrucciones SI, S2, etc. La instruccin cobegin (comienzo) termina cuando han terminado todas las instrucciones concurrentes. Cada instruccin Si puede ser cualquiera de las construcciones permitidas en el lenguaje, incluyendo las asignaciones sencillas o las llamadas a procedimientos. De invocar algn procedimiento, podrn pasarse datos a los procesos invocados mediante los parmetros de la llamada. La instruccin cobegin podra incluir, a su vez, una secuencia de instrucciones donde apareciera cobegin, y as construir una jerarqua de procesos. Cobegin puede encontrarse en occam2.

7.3.4 Declaracin explicita de procesos


A pesar de que cobegin y fork permiten expresar la ejecucin concurrente de rutinas secuenciales, la estructura de un programa concurrente es mucho mas ntida si las propias rutinas son quienes establecen su ejecucin concurrente. La declaracin explicita de procesos proporciona esta posibilidad. Otros lenguajes que soportan la declaracin explcita de procesos, como por ejemplo Ada, permiten tambin la creacin implcita de tareas. Todos los procesos declarados dentro de un bloque comienzan a ejecutarse concurrentemente al final de la parte declarativa de dicho bloque. En la anterior discusin se han resumido los modelos bsicos de ejecucin concurrente, y se ha hecho referencia a lenguajes que soportan ciertas caractersticas particulares. Con el fin de 8

proporcionar ejemplos concretos sobre lenguajes de programacin reales, se vera la ejecucin concurrente en occam2, Ada y Java. Complementariamente, se comentara la ejecucin concurrente en POSIX,

You might also like