60
~ROGRAMACION CONCURRENTE // 147686 PROYECTO 'TERMINAL I Y I1 LICENCIATURA EN COMPUTACION ASESOR: l?EDRO FLORES ALUMNO:: REN~MAC KINNEY ROMER~ __I UNIVERSIDAD AUTObNOMA METROPOLITANA IZATAPALAPA MEXICO

PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

  • Upload
    hakiet

  • View
    216

  • Download
    0

Embed Size (px)

Citation preview

Page 1: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

~ R O G R A M A C I O N CONCURRENTE //

147686

PROYECTO 'TERMINAL I Y I1

LICENCIATURA EN COMPUTACION

ASESOR: l?EDRO FLORES

ALUMNO:: R E N ~ M A C KINNEY R O M E R ~ __I

UNIVERSIDAD AUTObNOMA METROPOLITANA IZATAPALAPA

MEXICO

Page 2: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

CAPITULO I

CONCEPTO DE l?ROGRAMACION CONCURRENTE

Veamos primero unas definiciones:

CONCURRENCIA.- Simultaneidad de dos sucesos: concurrencia de circunstancias favorables. (Diccionario Larousse manual ilustrado, 1981)

PROGRAMA.- Grupo de instrucciones que indica a la computadora cómo realizar una función específica. (Glosario de Compu- tación, 1983)

Tomando como base estas dos defini- ciones podríamos dar otra:

PROGRAMA CONCURRENTE.- Grupo de instrucciones, donde un conjunto de ellas se ejecuta de manera simultánea, e indican a la computadora cómo realizar una función específica.

Podríamos en base a estas definir a la pro- gramación concurrente como el desarrollo de programas concurrentes, pero no es así.

i Qué beneficios reporta el poder ejecutar instrucciones simultáneamente ?

Tomemos como ejemplo un algoritmo simple de ordenamiento por intercambio [wIm

You my call it coalition, you my call it the accidental and fortuitous cmuwence of atoms.

Lord Palmrston

19861. Este algoritmo es secuencia1 y para n valores a ordenar el algoritmo hará aproxi- madamente n2/2 comparaciones para resolver el problema. Hagamos ahora el mismo análisis pensando que utilizando dos computadoras, ordenamos la mitad del arreglo en cada una y hacemos un ordenamiento por mezcla al ter- minar estas. El número de comparaciones hecho por cada computadora sera

(n/2)2/2 = n2/8.

El número de comparaciones en orde- namiento de mezcla ser5 n. Se necesitan por tanto (n2/8)*2 +n comparaciones para resolver el problema. En la Tabla 1 se hace una com- paración de estos valores para diferentes va- lores de n.

Como es fácil de observar en la tabla, el tiempo empleado en resolver el problema de manera secuencial será proporcional a 800 comparaciones que se efectúan. Mientras tanto, si utilizamos dos computadoras para resolver el problema nos llevaremos un tiempo pro- porcional a 440 comparaciones; y si ejecuta- mos la parte de cada computadora de manera simultánea nos llevará solo 240 comparaciones. Es sencillo observar que si tuviésemos una tercera commtadora Y una cuarta Dodríamos

Page 3: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

5

mejorar el número de comparaciones reali- zadas y, por tanto, el tiempo de ejecución del mismo. Podemos observar la mejora que ob- tuvo el algoritmo al ser planteado de manera concurrente pensando que se pueden ejecutar partes de 61 simultAnea o paralelamente. Es de notarse que aún ejecutando los dos ordena- mientos en una sola computadora se obtiene una mejora sobre el algoritmo antiguo.

En un sistema completo de computación se tienen diversos periféricos en los cuales se efectúan procesos de lectura y escritura. A s í , en las primeras computadoras, la computa- dora al necesitar efectuar una operacidn de entrada o salida tenía que perder tiempo mientras el equipo periférico la realiza'ba. Dotando a estos perifkricos de procesadores propios y con una interfaz adecuada, pode- mos tenerlos rea- lizando las ope- raciones de en- trada y salida por su cuenta y mien- tras esto sucede, utilizar el proce- sador central

todo un proceso concurrente.

Tenemos dos formas principales de pro- gramas concurrentes. Una de ellas es ejecutar cada proceso en su propia computadora o en su propio procesador, llamhdosele a esta manera procesamiento distribuido (distribu- ted processing). Este se utiliza en aplicaciones como la de reservación de boletos de avión, inscripcih en la Universidad y otras donde se tienen varios procesos trabajando de manera paralela, y tambiCn en procesos en los que se involucran varias computadoras. Ultimamente, se han creado redes de computadoras (net- works) donde varias trabajan a la vez comu- nichdose entre ellas y compartiendo recur- sos, tales como bases de datos, y otros; teniendo

posibilidades de

n n2/2 (n2/8)*2+n (n2/8)+n

40 800 440

1 O0 5,000 2,600

1,000 500,000 25 1,000

TABLA 1

240

1,350

126,000

para que efectúe otro trabajo. Este problema es el planteado por los sistemas operativos. A 61 se le han dado todo tipo de soluciones dentro de las cuales el enfoque concurrente ha permitido mejorarlas de manera superior al enfoque secuencial.

Existe ademAs otro tipo de aplicaciones donde el enfoque concurrente es necesario. A s í , en un programa de reservación de líneas a6reas es preciso tomar en cuenta dentro del diseño el que existen diferentes computa- doras que e s t h trabajando al mismo tiempo, procesando transacciones de manera simul- tanea, trabajando cada una de manera :se- cuencial, pero dentro del sistema, siendo el

que cada una de ellas pueda ac- ceder a recursos de otra compu- tadora de la red. Existen redes donde uno puede mandar imprimir textos con calidad pro- fesional y recibir después el docu- mento impreso

por correo. Con el abaratamiento del costo de los procesadores, así como con la aparición de computadoras personales que tienen facili- dades de teleproceso, han surgido programas de entretenimiento donde ya es posible inter- actuar a varios usuarios entre sí.

La otra forma de programación concu- rrente es compartir uno o m& procesadores de un solo sistema de cómputo repartiendo el tiempo de estos entre varios procesos. A esto se le denomina multiprogramación (multipro- graming). Este es el enfoque que se utiliza en el diseño de sistemas operativos así como en algunos sistemas de control industrial.

Page 4: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

La ejecución de programas concurrentes tiene dos problemas primordiales asociados: la manera en que los diferentes procesos se comunican entre sí y la forma en que se pueden sincronizar entre ellos para saber cuando pueden actuar y cuando no. Tomemos como ejemplo el algoritmo usado para el ordenamiento. Cada computadora hace parte del ordenamiento y una vez que acaban las dos, se hace un ordenamiento por mezcla de las dos mitades. Es necesario entonces, que de alguna manera las computadoras digan que ya han terminado con sus respectivos trabajos para poder realizar el último paso. TambiCn es necesario definir la representación interna que se va a utilizar y de que manera van a devolver los dos procesos sus resultados. Estos problemas no son de ninguna manera trivia- les y es objetivo de estas notas el estudiar las diferentes soluciones que se les han dado.

Dentro de los programas secuenciales la corrección se realiza probando los diferentes módulos que componen el programa. Una vez probados todos ellos podemos asumir el fun- cionamiento del programa principal. En los programas concurrentes no existe una forma de probar, no digamos asegurar, que el pro- grama funcionara. Esto se debe a que los módulos no se ejecutan de manera igual cada vez que se ejecuta el programa. Por ejemplo, en una corrida del programa se ejecutan los módulos M, y M, de manera paralela em- pezando y terminando al mismo tiempo. Sin embargo, el módulo M, hace uso de la impre- sora y cuando se ejecuta otra vez el programa el módulo M,, para poder utilizar la impre- sora, tiene que esperar mas tiempo que la primera vez. El resultado es que en esta segunda ejecución los módulos M,, M, y M4 se ejecutan antes de que termine M,. Es claro que no existe forma de saber en que orden se ejecutarán las instrucciones en un programa concurren- te y por lo tanto la depuración de estos pro- gramas se hace muy dificil. Se han desarrolla- do herramientas de tipo matemzitico pruebar estos programas [HOARE 19811. Estas herra-

6 mientas estan basadas en inferencias de tipo lógico donde se demuestra la validez de cada parte del programa.

Como se ha visto, los programas concu- rrentes generan diversos problemas que hay que solucionar para poder trabajar con ellos. Una vez vistos estos problemas podemos dar, ahora si, una buena definici6n.

PROGRAMACION CONCURRENTE.- Es el nombre con el que se conoce a las no- taciones utilizadas, así como a las tCcnicas para expresar programación concurrente y a la solución de los problemas de sincronizaci6n y comunicación resultantes.

Dentro de esta definición se hace men- ción a la notación que se utiliza para represen- tar la programación concurrente. Hasta el momento no la hemos mencionado, pero resulta natural la necesidad de representar de alguna forma dentro de los programas con- currentes, los procesos que se ejecutan en paralelo.

La notación utilizada en la programación concurrente varía de autor a autor, nosotros utilizaremos la propuesta por [BEN-ARI 19821.

También haremos mención a otras. Dentro del pseudocódigo normal con el que se crean los programas, introduciremos cambios que reflejen la programación concurrente.

Para expresar que varios procesos se ejecutan concurrentemente utilizaremos la forma:

comiencen

terminen p,; p,; p,

Donde los Pi representan los procedi- mientos que se van a ejecutar en paralelo. Al llegar a comiencen los procesos Pi se ejecutadn de manera concurrente y, una vez que todos los procesos finalicen, se reanudar5 la ejecución despues de terminen, de esta manera nuestro

Page 5: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

7 programa de ordenamiento queda así:

Programa ordena; /* a = arreglo donde se tienen los elementos a ordenar */ Proc burbuja(ini, fin); /* ordena por burbuja l o s elementos a[ini]..a[tin] */ R o c mezcla(ini, mitad, fin); /* mezcla ordenadamente los elementos a[ini]..a[mitad-I] con los elementos a[mitad]..a[fin] */ comienza

para i < - 1 a n haz lee(a[i]); comiencen

ordena( 1 ,n/2); ordena(n/2+ 1 ,n);

terminen mezcla( 1 ,n/2,n);

termina

Como se ve esta representación es bias- tante clara. Existen otros elementos de no- tación, los cuales se irAn presentando con- forme se necesiten. Existen otras notaciones utilizadas para los programas concurrentes, mediante las instrucciones fork y join. Fork es el equivalente a una llamada al proceso, devlol- viendo un identificador. Ese proceso se ejecuta hasta terminarse de manera concurrente c'on el programa que lo llamó. Dentro del pro- grama que lo llamó puede ponerse una ins-

trucción join que harA que espere hasta la finalización del proceso. Esta instrucción join en algunos sistemas ha sido implantada como espera(Pi) (wait(Pi)). Al Usar esta notación nuestro programa de ordenamiento queda:

Programa ordena; Proc burbuja(inici0,fin); Proc mezcla(inicio,mitad,fin); comienza;

p 1 < - fork(ordena( 1 ,n/2)); p2 < - fork(ordena(n/2+ 1 ,n)); join(p 1); join(p2); mezcla( 1 ,n/2,n);

termina;

Las instrucciones fork y join pueden apare- cer en cualquier parte del programa y por tanto, usadas sin cuidado, pueden hacer im- penetrable el código. Sin embargo, usadas de manera disciplinada, son instrucciones muy poderosas y debido a ello han sido implan- tadas en sistemas operativos como UNIX [a Y THOMPSON19741 y en algunos lenguajes tales como Mesa basado en PUL [LAMPSON Y REDELL 19801. TambiCn se han utilizado en sistemas de computadoras pequeñas como Amiga [ M Y W

19851. Por su parte existen implantaciones de comiencen y terminen en Pascal Concurrente [BRINCH HANSEN 19753 c + + [STROUSI-RUP 19861 así como Modula [WIRTH 19771

Page 6: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

8

CAPITULO I1

HISTORIA DE LA PROGRAMACION CONCURRENTE

En la historia de la computación los avances que se dan en el Area de programación van detrAs de los avances de la tecnología y muchas veces debido a ellos. La programación concu- rrente es un ejemplo de ello. Esta surge como solución a los problemas planteados por los sistemas operativos. Estos, a su vez, se dan debido al mejoramiento y cambio del Hard- ware en las computadoras. Una vez aparecida la programación concurrente, se encontraron Areas donde se podía utilizar, propiciando así su avance como entidad independiente. Usando un esquema muy general de la historia de la computación veremos los avances que se die- ron en la programación concurrente. Se ver5 que fueron muchas veces forzados por avances tecnológicos, los cuales no todos eran en el hrea de computación.

Primera Generaci6n ( 1945- 1955) : -Tubos de vacío y tableros de conexiones.

La primera computadora electrónica que se construyó fue la ENIAC (Electronic Nu- merical Integrator and Calculator). Esta era un monstruo con 18,000 bulbos, 70,000 resis- tores, y 10,000 capacitores, y que devoraba 150 kilowatts de energía. Ocupaba 15,000 pies cuadrados de superficie y pesaba 30 toneladas. El programarla era un esfuerzo formidable, que requería una completa familiaridad con el equipo así como ingenio y paciencia considera- bles. A pesar de que, tanto los datos que se le

History is a distillation of rumour Augustine Biwel

proporcionaban así como los resultados que obtenía, estaban en tarjetas perforadas, el programa era un cableado entre sus diferen- tes componentes que se llevaba normalmente uno o dos días de trabajo. Para una mAquina capaz de resolver problemas en cuesti6n de minutos era absurdo perder tanto tiempo en su preparación.

Es aquí cuando una persona que había ayudado en el proyecto como asesor, John Von Neumann, propuso un modelo según el cual los programas estarían dentro de la com- putadora. Este modelo revolucionó al con- cepto de las computadoras y permitió que nacieran las primeras computadoras moder- nas.

La EDSAC (Electronic Delay Variable Automatic Calculator) fue la primera compu- tadora en funcionar con las ideas de Von Neumann. Después surgirían varias mAs del mismo tipo, las cuales representaban un avance sobre la ENIAC. Pero en estas computadoras “primitivas” todavía no existían los sistemas operativos ni los lenguajes de programación. Los programas eran simplemente una cade- na de bits que se introducían a la computa- dora. Las m5quinas eran dedicadas ya que sólo ejecutaban un solo programa a la vez. El modo de utilizar un sistema así era llegar al centro de cómputo y reservar tiempo anotAndose en una hoja. El usuario era a la

Page 7: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

9 rhpida y eficiente. A s í , la sesión de trabajo consistía en transferir de tarjetas perforadas donde se escribía el programa a una cinta magnética con la ayuda de una computadora barata. Luego, leer la cinta con una computa- dora cara que realizaba las operaciones del programa y escribía los resultados en otra cinta. Finalmente, usar la cinta otra vez en una computadora barata que leía los datos de la cinta y los imprimía. Esta fue la solución generalmente adoptada y se le llamó sistema por lotes (Batch Systems).

vez programador y operador de la computa- dora y esperaba que durante la ejecución de sus programas no se fundiera ningún bulbo, ademPs de haber apartado el tiempo sufi- ciente para poder terminar. Los problemas resueltos eran, por lo general, chlculos matemhticos tales como obtener tablas de senos, cosenos y logaritmos. A principios de los 50 entró en el mercado la computadora UPJI- VAC (Universal Automatic Calculator) la cual tenía sistemas de entrada y salida independi- entes, lector de tarjetas UNTYPER e impre- sora UNIPRINTER. Esta también contaba con un sofisticado sistema de cinta magnética. La UNIVAC representó también la primera ins-talación comercial de una computadora, en una planta de General Electric. Dentro de la UNIVAC así como de las computadoras a p e le siguieron, cada vez que hacía un acceso a’los periféricos de entrada y salida (E/S) se debía esperar a que estos terminaran con la opera- ción que estaban realizando para poder conlin- uar con la ejecución del programa. Los pro- gramas, por lo tanto, variaban mucho en tiempo de ejecución, dependiendo direc- tamente del uso de E/S que hiciesen.

Segunda Generaci6n (1 955- 1965) - Transistores y sistemas por lotes.

La introducción de los transistores a. la mitad de los 50 cambiaron el paisaje raldi- calmente. Las computadoras fueron lo confa- bles necesarias para ser manufacturada:s y vendidas a compradores que pudieran usarlas lo suficiente para que la inversión fuera rentable. Por primera vez hubo divisiolnes bien establecidas entre las personas que diseñaban, construían, operaban, programaban y mantenían a las computadoras. Sólo granldes corporaciones industriales, gobiernos y uni- versidades pudieron comprar estos equipos, o hbricarlos. Al ser adquirido o construido equipo de este tipo por instituciones el tiempo fue compartido por mPs personas. Para los tlis- positivos de E/S surgieron los manejadores (drivers) que permitían su acceso de man’era

Por otra parte en 1954 surgió lo que se conoce hoy en día como el primer lenguaje de programación de alto nivel, FORTRAN. Es así que durante esta generación se vio el mejo- ramiento de los programas de sistemas: en- sambladores, cargadores, ligadores y los primeros compiladores, PUFFT, WATFOR y WATFIV. Estos facilitaron la programación, no así el uso de la computadora que se volvió mPs complejo. Se cargaba el compilador que producía un programa en ensamblador de un programa fuente y después se utilizaba un li- gador para “pegar” las diferentes rutinas que existían en bibliotecas con lo generado por el compilador. Finalmente, el programa objeto era ejecutado y depurado desde la consola como anteriormente se hacía. Es claro que gran parte del tiempo se utilizaba en montar y desmontar cintas magnéticas. Si sucedía algún error durante el procedimiento, se necesitaba empezar de nuevo. Es de hacer notar que en esta época se empezó a realizar la simulación de vuelos espaciales en compu- tadoras así como su seguimiento. Tales tareas se necesitaban hacer con muchas computa- doras y mucha gente ya que no se podía procesar la información en paralelo. Durante esta etapa de la computación toda la investiga- ción en este campo se hacía en universidades de todo el mundo, donde se realizaron inves- tigaciones que influirían notablemente poco después.

Page 8: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Tercera Generacilin (1 965- 1975)

-Circuitos integrados y multiprograma- ción.

En la segunda generación el costo de la computadoras se media en millones de dólares. Una IBM 7094 costaba $2,000,000 USD con una vida esperada de 5 años, lo que repre- sentaba $45.66 dólares de costo por hora usando la computadora 24 horas 365 días al año, tomando solo en cuenta el costo del equipo. Como es natural, los dueños de las computadoras deseaban utilizarlas el mayor tiempo posible. En la 7094 cuando un trabajo hacía uso de la cinta u otra operación de E/S, el CPU simplemente se detenía y esperaba a que terminara. Cuando se ejecutaban pro- gramas de tipo científico las instrucciones eran cAlculos maternaticos y muy ocasionalmente E/S, el tiempo desperdiciado era insignifi- cante. Para aplicaciones de tipo comercial, el procesamiento de las E/S llegaba a ser el 80% o 90% del tiempo total, y algo debía hacerse al respecto.

Este es, propiamente dicho, el surgimien- to de los primeros conceptos de programación concurrente. Para resolver el problema se pensó en tener una partición de memoria tal que en cada partición se ejecutara un diferen- te proceso. Cuando se tiene una instrucción de E/S el sistema “duerme” al proceso que la llamó y continúa con otro. La solución es muy compleja. Necesita Hardware que impida el acceso de una partición de memoria a otra y el Software que controle la ejecución de proce- sos. Este último tiene que hacerse con sumo cuidado ya que de no ser así, se puede llegar a un estado de abrazo mortal donde la compu- tadora queda completamente paralizada. Este sistema es el mencionado anteriormente de multiprogramación. A continuación de la multi- programación surge una forma similar que es la de tiempo compartido. En ella se tienen varios usuarios en diferentes terminales ejecutando diferentes actividades.

10 Una vez encontrada la solución, se em-

pezó a realizar trabajo teórico en base a ella. Dentro de este resaltan los trabajos de Dijkstra, Brinch Hansen, y C.A.R. Hoare. De este tra- bajo teórico surgen las bases de la programa- ción concurrente. En un principio se hizo el estudio de cómo interactúan varios programas ejecuthdose a la vez (concurrentemente) para que no se interfieran entre sí. Con el avance del Hardware se abatieron rApidamente los costos de los procesadores de t a l forma que fue posible dotar de procesadores propios a los diferentes perifericos que manejan las E/S. Esto conlleva el problema de comunicaciones que surgen entre los dos procesadores. Den- tro de la programación concurrente los prin- cipales temas planteados son estos precisa- mente: ejecución de programas en forma par- alela y las comunicaciones entre ellos. Es por esto que a raíz de el surgimiento de el sistema operativo RC 4000 [BRINCH &SEN 1970,19731 surge el primer compilador de un lenguaje concur- rente, Concurrent Pascal [BRINCHHWJSEN 19751. El Area de desarrollo de sistemas operativos sigue siendo generadora de lenguajes concurrentes como veremos al final de estas notas.

A partir de aquí, la historia de la pro- gramación concurrente se ha desligado un poco de la de los sistemas operativos. Ya que su uso se ha esparcido a otras Areas de desarrollo. Sigue, sin embargo, siendo parte primordial dentro de su diseño.

Una vez realizado el trabajo teórico sobre la programación concurrente se comenzó a utilizar en problemas en los que la solución de tipo secuencia1 es lenta e ineficiente, y en algunos casos mejoró notablemente la ejecución de programas en los que se incorporaron con- ceptos de concurrencia. Una de l a s Areas donde mAs auge empezó a tener fue en la simulación de sistemas, ya que en la vida real la mayoría de las cosas suceden de manera paralela. De esta manera se utilizaron simuladores cada vez mAs complejos en lugares como la NASA y el MIT entre otros.

Page 9: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Cuarta Generacih ( 1975-1980 ) -Computadoras personales y multiproce- sadores.

Con el surgimiento de circuitos cada vez mAs pequeños y poderosos, la aparición de sistemas con mAs de un procesador a precios accesibles fue impresionante. Esto lleva al sur- gimiento de sistemas operativos que manejan a varios procesadores. El tenerlos significa1 la ejecución de procesos en paralelo en forma real. Dentro de los sistemas operativos, sin embargo, tal característica derivó en el ma- nejo de múltiples procesadores con los qpe contara el sistema como un recurso más de la máquina.

Hacia mediados de los años 70 surgib la primera microcomputadora, la Altair. Esta computadora ponía al alcance de las personas un sistema completo de computación por slólo 5,000 USD. Sólo dos afios después podía uno comprarse un sistema más completo por la mitad de ese dinero. Las computadoras per- sonales se dividieron en dos mercados clara- mente definidos. Las computadoras caseras, donde dominaron el mercado Commodore, Atari y Apple; y las computadoras personales, de las que existen miles de fabricantes tras las huellas de la “Big Blue” (IBM) e intentos recientes de las marcas de microcomputa- doras de hacer mella en el estándar de IBM.

En estas computadoras personales se dio un fenómeno bastante interesante, en un prin- cipio las computadoras sólo se basaban en un microprocesador que se encargaba de todo. A s í , existieron las computadoras basadas en el Motorola 6502, tales como la Apple I[ y la serie PET de Commodore. Y por otro lado las basadas en el microprocesador de Intel., el 8080, como Kaypro, Xerox, Osborne y otras. Estas dos ramas evolucionarían eventualmente en los dos distintos mercados de los que se habló. Las computadoras eran construidas de manera flexible para que uno pudiese com- prar un equipo mínimo con el cual trabajar, y

11

posteriormente ir agregndole perifkricos para mejorar su eficiencia. En algunas computa- doras esto implicó el hacer que dos o mAs microprocesadores trabajasen en conjunto para realizar algunas funciones. En el caso del equipo PET se tenía a un 6502 manejando a la unidad de lectura y escritura de discos en comunicación con otro 6502, que era la com- putadora propiamente dicha. Estas computa- doras hicieron que el concepto de concurren- cia se volviera cotidiano y evolucionaron en esta Area al uso generalizado bajo una forma un tanto heterodoxa, los coprocesadores.

Los coprocesadores no son otra cosa que CPUs dedicados a trabajos específicos. A s í , hay coprocesadores de gráficas, entraddsalida, matemáticas, audio, manejo de memoria, etcktera. ? Son diferentes los problemas plan- teados por el manejo de coprocesadores en paralelo a aquellos de la concurrencia ? No demasiado. En el manejo de coprocesadores se enfrenta uno por lo general a facetas de la concurrencia. El problema de comunicaciones es común a todos, pero en el área de gráficas es mínimo y el de manejo de memoria es vital. Todos ellos tienen en mayor o menor medida semáforos, variables globales, fenómeno de exclusión mutua, problemas todos ellos de la programación concurrente.

Dentro del área de grandes computa- doras la aparición de máquinas con muchos procesadores no ha sido explotado en el 6rea de la programación concurrente. Estos gene- ralmente son tratados como un recurso más de la maquina y por lo tanto manejados a traves de los sistemas operativos. Los pro- gramas específicamente concurrentes son muy pocos y por lo general usados en aplicaciones de tiempo real donde son indispensables. No existen muchos compiladores concurrentes que estén a disposición de los usuarios en general. El uso de este paralelismo ha sido por tanto invisible al usuario en general.

Debido al bajo costo de procesadores surgió

Page 10: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

otra forma de paralelismo, el tener varios CPUs conectados a una red. Esto que lleva el nombre de sistemas distribuidos, ha sido per- mitido por el abaratamiento continuo de las computadoras en general. A nivel de tele- comunicaciones es, por tanto, lugar común el hablar de varios usuarios conectados al mismo tiempo a un computador dentro del cual tienen una conferencia, obtienen o donan programas de dominio público y tal vez se entretienen en un juego multiusuario. Esto que hace 10 años parecería fantAstico es hoy, gracias al desarro- llo de la programaci6n y de la electrbnica, algo natural.

12 Las bases estan dadas y lo Único que falta

es el uso de todas las herramientas ya existen- tes para apoyar el desarrollo de programas concurrentes cien por ciento. Programas diseñados, programados, compilados y ejecuta- dos en computadoras de manera concurrente. Es motivo de estas notas el dar las bases, ver algunos sistemas de computaci6n orientados a la concurrencia, así como de convencer a las personas interesadas en el uso de la pro- gramación concurrente en su uso como herra- mienta poderosa para la soluci6n de proble- mas en muchas Areas de la computaci6n.

Page 11: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

13

CAPITULO I11

EXCLUSION MUTUA

En este capítulo trataremos el primer problema que surgió en la programación concurrente, pero antes, veamos unas defini- ciones intuitivas. Supongamos que realizamos el viaje Cuernavaca-Taxco por la carretera “libre”. Dentro de ella existen puentes los cuales s610 los puede atravesar un coche a la vez. Debemos, por tanto, fijarnos que no venga un auto para poder atravesar esa parte noso- tros. Podemos pensar en estos puentes de un carril como recursos, que no pueden ser usa- dos de manera simuldnea por más de un coche. El uso del puente excluye que otro lo pueda utilizar. El cruzar el puente sería una secci6n critica dentro de nuestro viaje. Si en algún tramo de la carretera se descompone un coche tendremos un caso similar al arriba expuesto, es decir, que la carretera es un recurso que puede generar problemas de exclusidn mutua bajo ciertas circunstancias. Apliquemos estos conceptos intuitivamente a la programación. Tenemos así partes de prwe- sos paralelos en los que sólo se puede ejecutar una parte de ellos si el recurso que utilizan no está siendo usado por otro proceso. <Qué re- cursos generan problemas de exclusión nnu- tua? La respuesta depende del sistema. Si los procesos usan una variable común x es claro que sólo un proceso a la vez podrá hacer uso de ella. Si deseamos utilizar la impresora, sólo

Mas vale solo que mal acompañudo

Refrdn popular

un proceso lo hará a la vez. Si tenemos más de una impresora conectada al sistema, entonces podemos tener procesos usando tantas impre- soras como haya en el sistema. Existen tam- bién recursos que generan problemas de exclusión mutua bajo ciertas circunstancias. Podremos tener, por ejemplo, a varios proce- sos leyendo y escribiendo datos en un disco siempre y cuando no afecten al mismo archivo. Pero habr5 veces en que se quiera que lo hagan sobre una misma base de datos y se tendrán problemas de exclusión mutua. Por lo tanto, ser5 responsabilidad del programa- dor el especificar en qué momento se tiene un recurso que se desee utilizar de manera exclu- siva.

Dentro de los procesos existir5 en cada uno una pequeña parte que debe hacerse usando un recurso de manera exclusiva, con exclusi6n mutua. A esta se le llama secci6n crítica. Habr5 algunos sistemas en los cuales ciertas operaciones serán implícitamente ex- cluyentes y otros en los que todo ser5 a cargo del programador. Si ejecutamos el siguiente programa <qué pasará? (utili-zaremos la in- strucción comiencen a,, a2, ..., a terminen para denotar que a,, a2, ..., an se eJecutan en paralelo sin ningún orden y terminan hasta aue havan finalizado todos los a.. 1 5 i 5 n.

u.

Page 12: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

14 como se explicó en el capítulo anterior) Ahora veamos el siguiente programa.

Programa Prueba; var cuenta, i: entero; proc incrementa; comienza

termina; cuenta <- cuenta + 1

comienza para i <- 1 a 20 haz comiencen

incrementa; incrementa

terminen esc (‘El valor de x es ‘); esc (x)

termina.

Al ejecutar este programa usando un compilador concurrente lax, a diferencia de lo que podríamos pensar, no vale 40. Sus valores fluctúan entre 30 y 40 aproximadamente como se puede ver en distintas corridas. (Al final de las notas se puede observar la ejecución com- pleta de una corrida)

El valor de x es 38

El valor de x es 40

El valor de x es 39

El valor de x es 39

El valor de x es 36

El valor de x es 40

Si dentro de nuestra maquina la instruc- ción asignación es indivisible, es decir, que ser5 ejecutada toda de una vez (recordemos que una asignación corresponde generalmente a dos o tres instrucciones de lenguaje de mAquina), entonces el resultado es obvio, pero de otra forma dificilmente sabremos que sucede.

Programa Imprime; var x, y: entero; proc uno; comienza

x < - x + 1; esc (‘El valor de x es ‘); esc (x)

termina; proc dos; comienza

y <- y + 1;

esc (Y) esc (‘El valor de y es’);

termina;

comienza y < - o ; x < - o ; comiencen

uno; dos; dos; uno;

termina. terminen

<Cual es la salida del programa? Veamos varias ejecuciones del código anterior.

El valor de x es 1 El valor de y es 1 El valor de y es 2 El valor de x es 2

El valor de x es 1 El valor de y es El valor

El valor de y es 2 1 2

de x es

El valor de x es 1 El valor de y es 1 El valor de y es 2 El valor de x es 1

Page 13: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

El valor de y es El valor de x es 1 1 El valor de y es 2 El valor de x es 2

Nuevamente el resultado no es el esperado ya que x y y no tiene valores consistentes ademhs de que la salida a pantalla no es limlpia y se mezcla. Como se ve, lo que es excluyente es el uso del recurso, en el caso del dispositivo de salida, y no las instrucciones que se llevan a cabo. El propósito de estos ejemplos es el demostrar que es muy importante el encon- trar las secciones críticas de los procesos ya que de no hacerlo así se encontrarh muchos resultados inesperados.

Podremos decir ahora que el prob1e:ma de exclusión mutua queda definido entonces como la ejecución de procesos Pi con secciones críticas Crit,, las cuales no pueden traslaparse.

La solución a este problema no es trivial, ni mucho menos. Sin embargo, podríamos definir un protocolo a traves del cual los procesos “avisarhn” cuando van a entrar a una sección critica. A este protocolo le pm- dremos por nombre mutexbegin para cuando el proceso hace su entrada a la sección critica y mutexend para cuando sale de ella, lo que nos permite escribir nuestros programas como:

Proceso Pi; .... Instra; { mutexbegin; Sección

Criti; Crítica mutexend; }

Instr,;

Esto nos permite una manera elegante de programar así como dejar bien claro las secciones críticas de nuestros procesos. Nótese que esta es una abreviación de una secuencia de instrucciones mas complejas que forman el manejo de la exclusión mutua, las cuales vere- mos a continuación.

15 CASO DE DOS PROCESOS

PRIMER PROTOCOLO

Veamos el caso particular en el que sólo tenemos dos procesos concurrentes. A s í , ten- dremos P, y P, con sus respectivas Crit, y Crit,. AdemAs estos cuentan tambien con secciones no críticas antes y despues de las críticas, llamtmoslasAntesi y DespuCs,. Usaremos para resolver el problema una variable esta-en crit que nos dirh que proceso esta en su sección crítica. De tal suerte que solo actualizaremos a esta variable cuando entremos en nuestra sección crítica y al salir para permitir la en- trada al siguiente. Este serh el equivalente al mutexbegin y mutexend mencionados arriba, los cuales denotaremos en todos los algorit- mos que presentemos como [mb] y [me] con los corchetes al inicio y final de las secciones. El programa nos queda por tanto:

Programa Exc-mut; proc uno; comienza

repite

-

Antes,; [mb mientras esta-en crit = 2 haz;

3 esta ” en crit = 1 ;

[me1

{ No hagas nada }

Crit,;

esta ” en crit = 2; Despues,;

por siempre termina;

Page 14: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

proc dos; comienza

repite Antes,;

[mb mientras esta-en-crit = 1 haz;

] esta en crit = 2;

[me1

{ No hagas nada }

Crit,;

esta-en-crit = 1; Después,;

por siempre

"

termina;

comienza { Principal } esta en crit = 1 ;

comiencen uno; dos;

terminen

"

termina

Es claro que podemos prescindir de las partes Antes, y Después, ya que podríamos llamar a procesos dedicados a efectuar secciones críticas. Como se puede observar, este proto- colo permite el acceso de uno de los dos procesos a sus respectivas secciones críticas de manera exclusiva. No existe la posibilidad de que los dos lo hagan a la vez ya que esta-en-crit siempre tendrh un valor Único. Podíamos pensar en este protocolo como un pizarrón donde est5 escrito el número del proceso al que le toca entrar a su sección crítica. Por lo tanto, entrarh el proceso uno luego el dos y luego el uno y el dos y así sucesivamente. Esta condición, el que los procesos se ejecuten el mismo número de veces es el problema que tiene este protocolo. Si el proceso dos ejecuta su sección crítica una vez mas que el uno, el proceso se quedad en un ciclo infinito esperan- do a que el proceso uno entre a su sección critica una vez m4s. El pasar explícitamente de un proceso a otro, que este es el caso, es una técnica que se denomina corutinas. En ella existe una instrucci6n resume(p,). P, es capaz de detener su ejecución para pasarle el control a P- Y Dosteriormente P- le regresa el control

16 con una instrucci6n resume(P,). Esta técnica es útil para ciertos problemas de programa- ción pero no debe ser usado como sustituto para la programación concurrente. Esta primera solución se debe a [DIJWXA 19751.

SEGUNDO PROTOCOLO

Vamos a darle a cada proceso su llave para permitirle entrar a su sección crítica cuando no este el otro en él. Tendremos por lo tanto dos variables esta - P, y esta-P, que nos dirh que proceso se encuentra en su secci6n crítica mediante un valor de sí y no. Si un P, quiere entrar en su sección crítica verificar4 primero que esta-P, sea no. Si es así pondrh su variable esta - P, = si. Una vez que termine de ejecutar Crit, harh esta-P, = no. Tendremos a nuestro programa de la siguiente manera.

Programa Exc - Mutua - 2;

Proc uno; comienza

repite [mb mientras esta-P, = si haz;

{ No hagas nada } ] esta - PI = si;

Crit, = si; m 1

esta-P, = no; por siempre

termina;

Proc dos; comienza

repite [mb mientras esta P, = si haz;

] esta-P, = si; Crit, = si;

[me1 esta P, = no;

{ No hagasnada }

por siempre -

termina;

Page 15: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

17

comienza { Principal 1 esta-P, = no; esta - P, = no;

comiencen

terminen uno; dos;

termina

Parecería a primera vista que el problema se resolvió. Con este programa cada proceso tiene una variable independiente que le per- mite entrar a su sección crítica. Veamoslo a detalle ?Qu6 pasa si la secuencia de ejecución es estrictamente paralela o si se ejecuta ulna instrucción del proceso uno y luego una del dos, etc?

repite repite mientras esta-P, = si haz; mientras esta-P, = si haz;

Al llegar a este punto en ambos casos los resultados seran falsos y continuaran con su ejecución, entrando a sus secciones críticas, por lo que este protocolo todavía no es el adecuado. A este problema se le denomina de seguridad. Por lo que un sistema seguro stera aquel que no permita la violación de la exclu- sión mutua.

PROTOCOLO TRES

Lo que necesitamos es un protocolo que permita a cada proceso entrar a su sección crítica sin necesidad de que el otro lo haga, así como el asegurar que solo un proceso este en ella. Veamos el siguiente algoritmo:

Programa Excl Mut 3 Proc uno; comienza

repite [mb esta-P1 = si;

- -

mientras esta-P, = si haz comienza esta-P, = no;

{ No hagas nada por algunos momentos :}

esta P, = si; 1 termina

[me] esta-P, = no;

termina;

Proc dos; comienza

[mb esta - P, = si;

-

crit,;

por siempre

repite

mientras esta - P1 = si haz comienza esta P, = no;

esta - P, = si; { No hagas nada por algunos momentos 1

] termina crit,;

[me] esta P, = no; por siempre

termina;

comienza { Principal }

-

-

esta - P1 = no; esta-P2 = no;

comiencen

terminen uno; dos;

termina

En este algoritmo las variables esta - P no significan siempre que se est6 ejecutando la sección crítica de alguno de los procesos sino primero la intención de entrar y despues el ya estar adentro. El algoritmo por tanto pregunta primero si el otro proceso esta ya en su sección crítica y si es así espera a que termine. Si el otro también esta intentando entrar le permite el hacerlo declinando el hacerlo y luego reinten- tando entrar. Se puede probar que este al- goritmo es seguro. Sin embargo, en 61 surge un problema que no conocíamos. Dado que cada quien pregunta primero si puede entrar y luego declina en caso de que el otro pregunte si puede entrar, se puede dar el caso siguiente:

Page 16: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Inicialmente PI asigna esta PI P, asigna esta-P, PI verifica esta - P, P, verifica esta-PI PI asigna esta-P, P, asigna esta-P, PI asigna esta-P, P, asigna esta-P, P, verifica esta-P,

-

esta - PI

no si si si si no no si si si

esta - P,

no no si si si si no no si no

Se ve que este problema tambikn es poco probable que ocurra y es bastante factible que desputs de algunos momentos se corrija ya que las sincronización con que ocurren las cosas no puede ser tan perfecta, aunque sin embargo debemos de buscar otra solución. A este problema se le denomina de supervivencia y a los procesos involucrados, que se esthn muriendo de hambre. Ya que no pueden acceder a recursos, se hace la analogía con un recurso indispensable para los hombres, la comida.

PROTOCOLO DE DEKKER

Veamos finalmente la solución dada por el matemhtico [DEKKER 19701 a este problema. Usaremos una versión reducida debida a [PEIFRSFN 19721~ ligeramente modificada por [&LT1975].

Programa Dekker; Proc uno; comienza

repite [mb esta PI = si;

turno = P,; 3 mientras esta-P, = si y turno = P, haz;

-

{ Nada } crit,;

[me] esta-P, = no; por siempre

termina;

18 Proc dos; comienza

repite [mb esta-P, = si;

turno = PI; ] mientras esta-P, = sí y turno = PI haz;

{ Nada } crit 1 ;

[me] esta-P, = no; por siempre

termina;

comienza { Principal } esta-P, = no; esta-P, = no;

comiencen uno; dos;

terminen termina

Esta solución resuelve los problemas de seguridad y supervivencia de una manera compacta y eficiente. Se usa una mezcla de los protocolos uno y tres, usando la variable turno para decidir a quien le toca insistir en entrar a su sección crítica. La prueba de este al- goritmo la veremos en un capítulo posterior.

Page 17: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

19

CASO DE N PROCESOS problema. <Qué sucede si tenemos tres proce- sos y los dos primeros se sincronizar a que al

PRIMER PROTOCOLO

El primer intento de implantación de .un protocolo de exclusión mutua para n procesos de debe a [DIJKSTRA 19751 el cual generalizó el algoritmo usado por Dekker llegando a la siguiente solucibn.

Program Dijkstra; Proc proceso(y0); comienza

repite [mb repite

band[yo] = intento; mientras turno < > yo haz

si band[turno] = desocup entonces turno = yo;

band[yo] = en-sc; j = O;

haz j=j+ 1; ] hastaj >=n;

[me] band[yo] = desocup;

termina;

mientras (j < n) y (j =yo o bandu] >en-sc)

critl;

por siempre

comienza { Principal } para turno = O hasta n haz band[turno] = desocup; turno = O; comiencen proceso( 1); proceso(2);

proceso(n); terminen

termina

Esta solución cumple los requisitos de seguridad y de supervivencia tal como lo hace Dekker. Sin embargo surge un nuevo

terminar uno su sección crítica el otro entre? Lo que va a suceder es que el tercer proceso se quedara en el segundo ciclo de mutexbegin ya que la variable turno serA alternativamente 1 y 2. Esto se debe a que en el tercer ciclo se permite entrar a su sección crítica a los proce- sos cuando ningún proceso con un número menor estA en su sección crítica. Esto hace que exista una prioridad inherente a cada proceso dada por el número que tenga. A este problema se le denomina falta de justicia ya que en un principio cada proceso debe tener la misma oportunidad de entrar a su sección critica. Dentro de este algoritmo existiran procesos los cuales nunca entren a sus secciones críticas por esta falta de justicia.

PROTOCOLO DOS

Un protocolo que cumple la condición de justicia es el dado por [EISENBERG Y MCGUIRE 19801. En éste, cada proceso espera en el peor de los casos n-1 turnos teniendo n procesos. La implantación corrige el ciclo que producía el problema en el protocolo de Dijkstra. Fijémonos bien en los cambios.

Programa Eisen - McGuire; Proc proceso(y0); comienza

repite [mb repite

band[yo] = intento; j = turno; mientras j < > yo haz

si bandu] = desocup

otro j = (j+ 1) mod n;

j = O;

entonces j = turno

band[yo] = en-sc;

mientras (j < n) y (j =yo o bandti] c >en-sc) haz j=j+ 1 ;

hasta (j>=n) y (turno=yo o band[turno]=desocup);

Page 18: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

3 turno = yo;

[me j = (turno+ 1) mod n; crit,;

mientras (j< >turno) y (bandti] = desocup)

turno = j;

por siempre

haz j=(j+ 1) mod n;

3 band[yo] = desocup;

termina;

comienza { Principal } para turno = O hasta n haz bandrturno] = desocup; turno = O; comiencen proceso( 1); proceso(2);

proceso(n); terminen

termina

Como se ve lo que se hace en este al- goritmo es dejar explícitamente a turno como un valor mayor al proceso que termino su sección crítica. En el protocolo anterior se dejaba esto a los procesos que despues intenta- ban entrar dando prioridad a aquellos que tenían un número menor.

Existe una implantación de Petersen que hace uso de niveles, lo que permite a cada proceso ir aumentando la prioridad conforme transcurre el tiempo.

Ya se habló de que en todas los protocolos discutidos anteriormente se supuso a la in- strucción asigna como indivisible. A esto tambiCn se le denomina “interlock” de la memoria. Conforme han ido evolucionando los sistemas de hardware se vio la convenien- cia de usar cierto tipo de instrucciones direc- tamente en el microprocesador.

20 IMPLANTACION C O N TEST - AND - SET

Esta instrucción se implanto primero en una IBM 360/370 y es ya bhsica para el diseño de nuevos microprocesadores. Siendo encon- trada en todos los elementos de la familia 680x0 de Motorola, así como los 80x86 de Intel, siendo estas las principales compañías que existen en el mercado. Esta instrucción, TAS, permite el verificar el valor de alguna localidad de memoria y a la vez cambiar su valor. En la familia 680x0 permite el prender las banderas Z (operando = O) y N (operando > 128) a la vez que prende el bit 7 permitiendo una implantación de mutex muy sencilla.

- mutexbegin TAS Ocupada BNE- mutexbegin JSR Proceso

- mutexend M0VE.B #O,-Ocupada

O si lo pensamos en pseudocódigo lo veríamos como

mutexbegin: mientras

mutexend: ocupada = no; Test - And - Set (Ocupada) haz;

Como se puede ver la implantación es sencillísima y no necesita ninguna prepara- ción previa. Dado que la instrucción es indivi- sible sólo uno de los procesos podrh ejecutarla. Y todos los otros procesos esperarh hasta que termine. La única desventaja de esta solución es que no se asegura la justicia del protocolo, lo cual se podría soslayar un poco ya que el comportamiento de este modelo estaría com- pletamente dado por el azar.

Page 19: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

OTROS PROTOCOLOS

Como es evidente existen otros proto- colos mas avanzados que aseguran las tres propiedades basicas:

1 .- Seguridad: Que sólo un proceso entre a su sección crítica a la vez.

2.- Supervivencia: Que no exista el peligro de que los procesos “se mueran de hambre”

3.- Justicia: Que todo proceso eventualmente entre en su sección crítica.

Estos protocolos por lo general contem- plan casos como el de que un proceso se detenga en su sección crítica, debido tal vez a un error en la programación. Este problelma se podría pensar como en una variante de supervivencia ya que sí un proceso no sale de su sección critica impedira pasar a los demias.

21 Durante todo este capítulo las soluciones

dadas al problema de exclusión mutua se basaron en procesos que contenían alguna instrucción del tipo:

mientras <Condición> haz; { Nada }

A este tipo de protocolos se les denomina de espera activa, “Busy Waiting”, ya que a pesar de que el programa no se encuentra haciendo nada, esta utilizando tiempo del procesador. Esto puede ser una limitante sería si la computadora donde se pretenden im- plantar tiene costos de CPU muy elevados. Se vera que en las primitivas usadas a continua- ción todas ellas dejan de lado este enfoque, ya que como se ver5 no es demasiado costoso.

1 4 7 6 8 6

Page 20: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

22

CAPITULO IV

SEMAIFOROS

Regresemos a nuestro ejemplo del capitulo anterior del viaje por la carretera. ?Cual es la manera mas facil de resolverlo en el mundo real? Utilizar un semaforo. h i , si un coche desea pasar por el puente de un solo carril, debe esperar a que se ponga en verde el semaforo. Si existen mas esperando pasar, tendremos que cada coche lo har5 cuando se cumpla que el semaforo esta en verde, lo que no asegurar4 que ningún coche este pasando su sección crítica. Si pensamos en el cumpli- miento absoluto de las condiciones arriba ex- puestas, es decir, que cada coche esperara a que no haya ningún coche sobre el puente para pasar, entonces no existe mucha diferen- cia si decimos que una vez que pasa el coche cambia el semaforo para permitir el paso de un coche del otro lado o, mas aún, que se ponga verde de ambos lados y cambiando a rojo una vez que un coche ha llegado a la sección critica. Podríamos resumir nuestra solución así. Tenemos un semaforo que per- mite el paso de un coche de una cola de ellos. Una vez que pasa el coche cambia el semaforo para permitir que pasen los dem5s. Esta es una idea intuitiva del como funcionan los semaforos en la programación concurrente.

Fue [DIJKSTRAOP. cm.] el que, tras haber estu- diado a fondo los protocolos de espera activa, logró desarrollar un nuevo esquema de sin-

cronización. Este enfoque ayudó decidida- mente al avance de la programación concu- rrente ya que su implantación no es dificil y permitió su extrapolación hacía conceptos mas complejos.

La idea de los semaforos es bastante sen- cilla y es como sigue:

Los procesos que estan esperando su en- trada a una sección critica se “duermen”. Son “despertados” por una señal del semaforo que les permite seguir adelante.

Los semAforos se representan como varia- bles de tipo entero los cuales tienen valores no negativos. Se les asigna al inicio un valor y después son modificadas por dos funciones:

wait(s): S = S - 1. Si s<O pone a “dormir” al proceso que la llamó.

signal(s): S = S + l. Si existe algún proceso que fue “dormido” por una llamada a wait(s) entonces lo “despierta”.

Estas operaciones fueron originalmente llamadas P (= wait ) y V (= signal). Nosotros las llamaremos wait y signal por que resulta más naturales. Ambas operaciones son in- divisibles. En la implantación que se haga de

Page 21: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

23 El problema de seguridad queda resuelto

ya que si dos procesos desean entrar a sus secciones críticas al mismo tiempo s610 uno de ellos podra entrar ya que la instrucci6n wait es indivisible. Y por lo tanto, el que no logre entrar será “dormido”. Esta soluci6n no causa problemas de supervivencia ya que una vez que termina un proceso su secci6n crítica, hace que el proceso que estii esperando se “despierte” y entre. Tampoco existe la posi- bilidad de que un proceso se “muera de hambre” ya que siempre uno entrara a su sección crítica y por lo que se dijo primero todo proceso sera capaz de entrar a su sección crítica en un tiempo finito. Dado que la solu- ción es sobre dos procesos el problema de justicia no es relevante. Supóngase que el proceso P, entra a su sección crítica y sale de ella. Despues, si quieren entrar los procesos P, y P, uno de ellos es suspendido. En el caso de que sea P, una vez que termine PI su secci6n crítica entrará P,. Podemos darnos cuenta de que toda la injusticia que se puede dar en este caso va a depender de que tantas veces entre cada caso a su sección crítica.

ellas se debe cuidar que se obtenga justicia ya que la definici6n no la menciona. Esto se logra generalmente poniendo en una cola a los procesos que estan esperando ser desperta- dos. Existen algunos trabajos de implantaciorles más sofisticadas que despues se mencionarh.

Existe un caso especial de semaforos y es cuando estos s610 pueden obtener dos valores, O y 1. A estos semaforos se les denomina binarios y se puede demostrar que podemos emular un semaforo de cualquier orden.

{Cómo se resuelve el problema de exclu- si6n mutua usando semhforos?

EXCLUSION MUTUA CON SEMAFOROS CASO DE DOS PROCESOS

Programa Semaforo. var S : semsforo;

proc p,; comienza

repite wait( s) ; crit,; signal(s);

por siempre termina;

proc P,; comienza

repite wait(s); crit,; signal(s);

por siempre termina;

comienza { Principal } S = 1; comiencen

terminen termina.

p,; p,;

El problema que tantos dolores de cabeza dio en el capítulo anterior es ahora resuelto de una manera sencilla y elegante. El programa además obtiene una simetría que permite la mas fácil revisión del uso correcto de los semaforos. Se habló en el capítulo anterior que el uso de los recursos es excluyente. {Que sucede cuando deseamos usar la impresora y leer datos de la terminal? Con las implan- taciones de espera activa necesitábamos tener varios juegos de variables que nos permitie- ran hacer mutexbegin y mutexend para cada recurso, lo que nos puede llevar a una solu- ción bastante compleja. Con el uso de semaforos podemos tener algo como lo que sigue:

Page 22: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Programa Usa - Recursos; var imp, ter : semaoro;

proc Lee-Esc; comienza ... wait(ter); lee (datos); signal(ter);

wait(imp); esc (datos); signal(imp);

...

... termina

La solución es entonces simple por que podemos tener una variable de tipo semsforo para cada recurso que queramos utilizar de manera selectiva. Esta solución nos puede llevar, sin embargo, al problema cl4sico de la programación concurrente, el abrazo mortal. Este problema se ver5 a detalle en el capítulo VI11 de estas notas. Por lo que cuando haga- mos referencia a 61 solo lo haremos de manera superficial.

CASO DE N PROCESOS

Programa Semsforo; var S : semsforo;

proc P(i); comienza

repite wait(s);

crit; signal(s);

por siempre termina;

24 comienza { Principal }

S = 1; comiencen P(1); P(2);

P(n);

termina. terminen

La solución es tan sencilla como la que se obtuvo anteriormente. El problema que se presenta aquí se debe a la implantacih que se tenga de los semsforos. Si no se tiene una implantación justa podemos tener a algunos procesos que nunca entren a sus secciones críticas. Si sucediera que al despertar a algún proceso por medio de signal se hiciera al que tuviera el índice menor tendríamos el mismo caso discutido en el capítulo anterior con algunos procesos muritndose de hambre.

Las implantaciones que se hacen con los semsforos crean por lo general una cola de procesos suspendidos. (Recordemos que una cola es una estructura del tipo FIFO “First In First Out”, es decir, el primero en entrar es el primero en salir.) Y de esa forma se asegura la justicia para una solución de estas (No vere- mos aquí la demostración, pero es buen ejer- cicio el probarlo aunque sea de manera intui- tiva).

Se habían mencionado sistemas en los que se cuenta con mAs de una impresora y por lo tanto podríamos tener a m& de un proceso entrando a una sección crítica donde imprimi- era. La solución sería el darle un valor inicial a S que fuera igual al número de impresoras, o para el caso recursos, que se tuvieran. Esto tambiCn se puede hacer en base a semsforos binarios pero su solución es dificil.

Page 23: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

SINCRONIZACION

Dentro de la programacih concurrente otro problema que es muy importante se refiere a la sincronizaci6n. ¿A que se refiere este problema? Supóngase que tenemos una empresa que transporta bienes manufactura- dos. Transportamos materias primas de A a B y manufactura de B a A. Es claro que para abatir costos esperemos en cada punto a que se produzca lo mhs posible de lo que vamos a transportar. Si estamos en A esperarem0.s a que se produzcan materias primas y las lleva- mos a B. Llegamos a B entregamos nuestro cargamento y esperamos a que se produzcan manufacturas suficientes para llevarlas a A. Si contamos con mhs de un camión tendremos que cada uno harh eso. Si deseamos compu- tarizar nuestra empresa necesitaremos un programa que nos diga cuAndo fue producido suficiente carga en A ó en B y si hay algún camión que esté sin carga nos lo diga. Este programa nos permitiría el “sincronizar” a la producción de carga con su transporte. E.ste sería, de una manera muy elemental, el problema de sincronización. En éI tenemos procesos (camiones), a los que llamaremos consumidores, que necesitan saber cuhrtdo otro proceso (las fábricas), que serán los pro- ductores, han generado información (carga) que ellos necesitan. Notemos que en el plan- teamiento que hicimos, los camiones espera- ban a que se fabricara la carga suficiente para poder transportarla. ¿Que pasa en el ciaso contrario, es decir, si la fhbrica tiene la carga lista antes de que lo este el camión? Evidente- mente seguirhn produciendo guardando esa producción en un almacén. En la prograrna- ción concurrente no implica mucha diferen- cia la política que se adopte. El enfoque de esperar a que el productor tenga lista la infor- mación generada y el consumidor esté listo para usarlo es usado a traves del rendezvous (encuentro) de Ada. Nosotros veremos el caso en que la sobreproducción se guarda en un almacen.

25 Existen dentro de la computaci6n entes

llamados buffers (los cuales nadie se ha puesto de acuerdo en como llamarles en español). Estos son segmentos de memoria común al productor y al consumidor los cuales sirven para guardar informaci6n temporal. Estos serhn el equivalente a nuestros almacenes. Habrh que tener cuidado al definir el tamaño de un buffer ya que si es demasiado grande se desperdicia memoria. Y, si por el contrario, es demasiado pequeño se pueden tener serios problemas de cupo los cuales se traducen en el congelamiento de los procesos productores.

Para facilitar el uso del buffer lo tomare- mos como una lista circular la cual tendrh como frente el primer elemento disponible para ser consumido y como atrhs el último ele- mento producido.

Pensemos en que S nos indique el tamaño de la cola. El tamaño inicialmente serh cero e ir8 aumentando y disminuyendo de acuerdo al consumo y producción que haya pero nunca ser5 menor a cero. El comportamiento de ella podría pensarse como el de un semhforo en donde cada vez que se produce se da la señal de que así se hizo y al consumir se espera a que haya elementos en el buffer. Para esta primera solución asumiremos que existe bastante es- pacio en el buffer como para que nunca se de un problema de sobrecupo. Esta solución im- plantada con semhforos queda como:

Programa Prod-Cons- 1 ; var mutex, elem :semAforo; proc productor; comienza

repite produce; wait(mutex);

almacén[atrhs] = ele-prod; atrhs = atrhs + 1 ; signal(mutex); signal(e1em);

por siempre termina

Page 24: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

26 proc consumidor; comienza

repite wait(elem); wait(mutex); ele cons = almacén[frente]; frente = frente + 1 ; signal(mutex); consume;

por siempre

-

termina

comienza { Principal ] elem = O; mutex = 1 ; comiencen productor; consumidor; terminen

termina

Es claro que d ado que usamos un ,a Brea de memoria común a los dos procesos usemos exclusión mutua para impedir resultados inesperados. La solución es bastante clara y simple. El semaforo elem tendra por lo tanto en todo momento el número de elementos que estén listos para consumirse.

Dentro del mundo real tendremos siempre limitantes y por lo tanto nuestro buffer no podrh tener una longitud infinita como en la solución anterior. Lo Único que necesitare- mos es utilizar la función módulo para lograr que cola sea cíclica. A esta restricción se le denomina bounded buffer. Veamos la solu- ción con esta restricción.

Programa Prod-Cons - 1 ; var mutex, elem :semBforo;

proc productor; comienza

repite produce; wait(mutex); almacén[atrAs] = ele prod; atras = atras mod T-MAÑO + I ; signal(mutex); signal(e1em);

por siempre termina

proc consumidor; comienza

repite wait(elem); wait(mutex);

ele cons = almacén[frente]; frente = frente mod TAMAÑO + 1 ; signal(mutex); consume;

por siempre termina

comienza { Principal } elem = O; mutex = 1 ; comiencen productor; consumidor;

terminen termina

La variable TAMAÑO tendra la longitud del buffer almacén. Esta solución contiene un error. ZuBI es? ?Que sucede si se llena la cola? Si se llena la cola, cuando se produzca un elemento, este se escribirB en una casilla ya ocupada por un elemento previamente pro- ducido.

Page 25: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

27 Por lo tanto, necesitaremos otro semhfcn-o

que nos permita el saber cuanto espacio tene- mos aún en el almactn para poner ahí la produccih que se obtenga. Esta sera la solu- ción mas adecuada para este problema y un buen ejemplo del uso de semaforos como sistema de sincronización.

Programa Prod Cons 1 ; var espacio, mutex, elem: semsforo;

- -

proc productor; comienza

repite produce; wait(espaci0); waiqmutex);

almacCn[atrPs] = ele prod; atras = atras mod TAMAÑO + 1; signal(mutex); signal(e1em);

por siempre termina

proc consumidor; comienza

repite wait(e1em); wait(mutex); ele - cons = almactn[frente]; frente = frente mod TAMAÑO + 1; signal(mutex); signal(espaci0);

consume; por siempre

termina

comienza { Principal } elem = O; espacio = TAMAÑO; mutex = 1;

comiencen productor; consumidor;

terminen termina

Lo Único que agregamos fue un semaforo que permite saber si aún queda espacio en donde poner elementos que se esdn pro- duciendo. Ffjese en la simetría de la solución así como en la claridad que se obtiene usando semaforos. Sin embargo, los semaforos obli- gan al programador a verificar que la solución sea operante dado el bajo nivel que aportan.

Finalmente, la estructura de datos usada como almactn es irrelevante ya que se puede utilizar cualquier otro. Queda como ejercicio el hacer una implantación de esta solución utilizando una pila. La cual es utilizada en la vida real ya que se utiliza un LIFO para el manejo de inventarios de tipo UEPS (últimas entradas, primeras salidas).

Page 26: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

28

CAPITULO V MONITORES

Los semhforos representan una manera eficiente y elegante de sincronización a bajo nivel. Su principal deficiencia es que el pro- gramador es el que tiene que verificar que su uso sea el adecuado. A s í , si en un sistema de 2000 líneas de código, nos falta poner un signal o un wait, es probable que nos percate- mos de ello una vez que tengamos el pro- grama en operación o tal vez tardemos meses en darnos cuenta de que es lo que falla. Otro problema que tiene es que los sem3foros se usan indistintamente para manejar exclusión mutua y sincronización de tal manera que no podemos estar seguros en cierto momento del significado de una instrucción wait o signal. Estas deberían tener notaciones distintas ya que se refieren a concepciones distintas. Dentro de este capítulo veremos dos formas m& estruc- turadas de manejar la concurrencia.

REGIONES CRITICAS

La propuesta de las regiones críticas [Hoare, 1972; Brinch Hansen 1972, 19731 deja de lado las dificultades producidas por los semaforos al utilizar una notación estructu- rada para manejar la sincronización. Las variables comunes son agrupadas en conjun- tos llamados recursos. Cada variable común puede estar en a lo mhs un recurso y puede ser

utilizada en instrucciones de regiones críticas (IRC). La exclusión mutua es garantizada para que las diferentes IRC no se traslapen. La sincronización condicional se logra a instruc- ciones explícitas de condición dentro de los IRC.

Un recurso r que contiene variables vI,v2,...,vn se declara como:

recurso r :v,, v2, ..., vn Las variables en r pueden ser utilizadas

sólo dentro de IRC que llamen a r. Tales in- strucciones tienen la forma:

regi6n r cuando C haz I

Donde C es una expresión condicional e I una lista de instrucciones (tambih pueden aparecer variables locales dentro de los IRC). Una IRC detiene la ejecución del proceso hasta que C es verdadero; I es entonces ejecutada. La evaluación de C y la ejecución de S no pueden ser interrumpidas por otros IRC que llamen al mismo recurso.

A s í C esta garantizado que es verdad cuando la ejecución de I empiece. Se asume que el mecanismo de espera se asume que es justo; un proceso que espera una condición C que es continuamente verdadera podrh

Page 27: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

29 proseguir en un tiempo finito. expresión condicional de aquellos procesos

que usan el buffer debe ser satisfecha antes de Veamos ahora cómo queda nuestro pro- poder entrar y una vez adentro el sistema nos

grama de productores y consumidores usando permite estar seguros de que hay exclusión regiones críticas. mutua.

Programa Prod Cons IRC; var buffer[TM-AÑO]:elementos; recurso buffer: almacCn;

proc productor; comienza

repite produce; regi6n buffer cuando

tam - alm < TAMAÑO haz almactn[atr8s] = ele prod; atras = atras mod TAMAÑO + 1 ; tam - alm = tam alm + 1;

termina por siempre

-

termina

proc consumidor; comienza

repite regi6n buffer cuando tam alm > O haz ele cons = almactn[frente]; frente = frente mod TAMAÑO + 1 ; tam - alm = tam alm - 1 ;

termina consume;

por siempre

- -

-

termina

comienza { Principal } tam-alm = O; frente = O; atras = O;

comiencen productor; consumidor;

terminen termina

Podemos observar que la exclusión mu- tua ha sido separada de la sincronización. La

A pesar de que la solución de las regiones críticas tienen muchas virtudes, puede ser muy costosa su implantación. Esto se debe a que las condiciones en los IRC pueden con- tener referencias a variables locales, y cada proceso debe evaluar sus propias condiciones. En un ambiente de multiprogramación esto se convierte en frecuentemente guardar el es- tado de los procesos, muchos de los cuales pueden ser improductivos porque despues de activar el proceso podemos encontrar que su condición aún es falsa. Si cada proceso es ejecutado en su propio procesador y se tiene memoria compartida se puede hacer una ¡m- plantación con espera activa a bajo costo.

Las regiones críticas han sido utilizadas en lenguajes como Edison que esta diseñado para sistemas con multiprocesadores. Algunas variantes se han utilizado en Distributed Proc- esses y Argus.

MONITORES

Una desventaja de las regiones críticas es que las llamadas est5n dispersas por todo el sistema. Esto implica que se tiene que estudiar todo el programa para poder encontrar la forma en que es utilizado un recurso.

El monitor por otra parte es un enfoque centralizado de los recursos. Si se desea hacer uso de un recurso, se le pide permiso al monitor, el cual puede decidir si esta ocupado o no y de ahí darnos el visto bueno o negarlo. La implantación de monitores depende en gran parte de facilidades dadas por el Hard- ware que aseguren la posición privilegiada del monitor: que el monitor se ejecute de manera interrumpida para garantizar exclusión mu-

Page 28: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

tua; sólo el monitor puede acceder a ciertas 5reas de la memoria; solo 61 puede ejecutar ciertas instrucciones tales como E/S.

Un monitor est6 compuesto por las de- finiciones de los recursos que maneja, así como de las operaciones que se pueden hacer sobre estos recursos. Dentro de esta definición tambi6n se cuenta con instrucciones que se ejecutan antes que otras para permitir la co- rrecta iniciaci6n de los recursos.

Dentro de un sistema se pueden tener varios monitores para manejar recursos que no sean mutuamente excluyentes entre sí. Esto hace más eficiente nuestro sistema, per- mitiendo a distintos monitores ejecutarse concurrentemente entre sí y es más seguro ya que un cambio en un monitor no afecta a los dem6s.

Una de las ideas que existen detrás de los monitores es la de estructurar los datos y las operaciones que se realizan con ellos. En C , por ejemplo, el uso de datos para realizar operaciones no está restringido y por tanto podemos sumar enteros con caracteres y a eso sacarle una raíz cuadrada o elevarlo a cualquier potencia. En Pascal, tenemos tipos que nos permiten el asegurarnos de que ciertas opera- ciones no puedan ser ejecutadas más que con un cierto tipo de datos.

Existen lenguajes, como Simula, que per- miten definir a los datos con todo y sus opera- ciones que se pueden realizar sobre ellos. Un monitor estaría cerca de esta definición ya que nos permite el definir cierto tipo de datos, los recursos, así como las operaciones que se pueden realizar sobre ellos. Este enfoque es muy útil y permite el completo control sobre la información que manejamos.

30 Tenemos la definici6n formal de un

monitor:

Un monitor se compone de un conjunto de variables (globales) seguido de un conjunto de procedimientos (que pueden ser param6tri- cos). El monitor tiene un cuerpo que es una secuencia de instrucciones que es ejecutado inmediatamente cuando el programa es ini- ciado. Este se utiliza para dar valores a las variables del monitor.

Las variables del monitor son directamente asequibles s610 dentro de los procedimientos del monitor. Para hacer acceso al monitor, se llama a uno de los procedimientos del moni- tor. Dentro del monitor se encuentran opera- ciones que permiten la sincronizacih sus procedimientos.

Programa productor - consumidor; monitor almac6n;

var b[TAMAÑO] : entero; entra, sale : entero;

proc agrega(e1em); comienza si n = TAMAÑO + 1 entonces { El buffer está lleno } espera no lleno

b[entra] = elem; entra = entra + 1 mod TAMAÑO; n = n + l ;

avisa " no vacío termina

"

proc toma(e1em); si n = O entonces { El buffer est6 vacío } espera " no vacío

elem = b[sale]; sale = sale + 1 rnodTAMAÑ0; n = n - 1 ;

avisa no lleno termina "

Page 29: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

1 4 7 6 8 6 31

comienza { Monitor } entra = O; sale = O; n = O;

termina { Fin del Monitor Almacén }

proc productor; comienza

produce(prod); agrega(prod);

termina

proc consumidor; comienza

toma(cons); consume(cons);

termina

Dentro de esta solución podemos obser- var que una vez planteadas las operaciones basicas sobre el almacén, es bastante f5cil el definir nuestros procesos.

La solución tiene varias ventajas de estruc- tura. Dado que la exclusión mutua es garanti- zada automaticamente por el monitor, no hay error posible de omitir un signal. Si nos olvida- mos de poner un termina nos generará un error de compilación como cualquier pro- grama normal. De tal manera que una vez que el monitor esté correcto, lo estar5 también cualquier llamado a él que se haga desde cualquier parte. En el caso de los semhforos dependía de cada uso que se les diera el verificar en cada parte ver si estuvieran o no correctos.

Para la sincronización necesitamos co- mandos del tipo wait I signal. Los comandos de los sem5foros servían a dos propósitos. Uno era proveer una forma de suspender y reacti- var procesos y la otra el llevar un contador. Dado que los contadores est5n ahora explíci- tamente contenidos dentro de la estructura del monitor, es suficiente con que se pueda suspender y reactivar procesos. Tal y como

puede ser utilizados muchos sem5foros en un programa, dentro de un monitor se pueden utilizar varios sem5foros. Definimos por lo tanto variables de tipo condici6n. Si c es de tipo condición tendremos dos operaciones que podamos hacer con ella: wait(c) y signal(c). Estas no deben ser confundidas con los semaforos por lo que les podremos el prefijo mon (mon.wait, por ejemplo) que funcion- ar5n de la manera siguiente:

mm.wai¿(c) El proceso que lo llamó es suspendido y entra en una cola de procesos suspendidos por la misma condición, esto es, que tambitn han ejecutado mon.wait(c).

mm.signaZ(c) Si la cola para c no esd vacía entonces activa el primer proceso en ella.

Si no hay procesos esperando al ejecutar mon.signal(c) entonces la instrucción no hace nada ni deja huellas visibles de su paso. La instrucción mon.signal(c) debe ser la última instrucción en ejecutarse dentro del proced- imiento. Esto para asegurar que el proceso reactivado comience después de que el proceso que estaba adentro s a l g a de su exclusión mutua.

Condición de Entrada Inmediata. Nuestro monitor debe verificar que después de ejecutarse un mon.signal(c) el proceso que se reactivó sea el primero en entrar al monitor, a pesar de que pueda haber otros procesos esperando entrar.

Veamos por qué es necesaria esta condi- ción. El proceso P, ha realizado un wait(c) y esta en una cola de espera. Si un proceso P, ha avisado que la condici6n c se cumple y sale de su sección crítica. Si una vez que ha sucedido esto se le permite la entrada a un proceso P, que no est5 en la cola, éste podría cambiar el estado de la condición a falso. De tal manera que al entrar en su sección crítica el proceso P2 la condicibn que debería ser verdadera no lo es generando un problema de seguridad. Veamos cómo queda la solución al problema

Page 30: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

32 de productores-consumidores usando moni- Existe una instrucción dentro de los mo- tores. nitores que nos permite definir un orden de

prioridad dentro de las llamadas que se hagan. Programa productor - consumidor; Tal instrucci6n fue propuesta por [ ~ E R M A N

monitor almacCn; 19721. Dado que su uso es complejo y el usarla var b[TAMAÑO]: entero; puede causar problemas colaterales aquí solo

entra, sale : entero; la mencionamos. no - lleno, no - vacío : condiciones;

proc agrega(e1em); comienza

si n = TAMAÑO + 1 { El buffer esta lleno } mon.wait(no - lleno) b[entra] = elem; entra = entra + 1 mod TAMAÑO; n = n + l ;

mon.signal(no - vacío) termina

proc toma(e1em); comienza

si n = O { El buffer esta vacío }

elem = b[sale]; sale = sale + 1 mod TAMAÑO; n = n - 1 ;

mon.signal(no lleno) termina

mon.wait(no-vacío)

-

comienza { Monitor } entra = O; sale = O; n = O;

termina { Fin del Monitor Almacén }

proc productor; comienza

produce(prod); agrega(prod);

termina

proc consumidor; comienza

toma(cons); consume(cons);

termina

SIMULACION DE SEWOROS

En un semaforo tenemos a un cierto re- curso, la variable del semaforo, que tiene ciertas instrucciones asociadas, wait y signal. Estas instrucciones deben ser mutuamente excluyentes. <Les recuerda algo? La anterior es una definición de un semaforo, pero tam- bién podría serlo de un monitor que maneja el recurso semaforo. Es claro, de manera intui- tiva, que la implantación de semhforos utili- zando monitores no debe representa ningún problema. Y ademas nos permite que en ambientes de programación donde tenemos monitores podamos usar semaforos donde sean mas convenientes y claro su uso.

La siguiente es una implantación de semdforos usando el enfoque del monitor. La implantaci6n que se hace es de semaforos binarios, pero se puede extrapolar a una donde se manejen cualquier tipo de semaforos. Las instrucciones se han utilizado con sus nombres originales P y V para evitar confusiones.

Programa exclusión-mutua; monitor simula semaforos;

var esta : booleano; -

no esta : condiciones; -

proc V; comienza esta = falso;

mon.signal(no esta); termina

-

Page 31: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

33

que un monitor no es m6s poderoso que un sem6foro y que el utilizar un enfoque u otro dependería únicamente de la dosis de claridad y confiabilidad que se obtiene gracias a alguno de los dos enfoques. La simulación del moni- tor se refiere únicamente al comportamiento que es visible para el usuario y no así a las protecciones de variables que debe aún ser implantada. Esto se puede dar como una clase en Simula o un package de Ada.

proc P; comienza

si esta entonces mon.wait(no-esta); esta = verdadero; termina

comienza { Monitor } esta = falso;

termina { Fin del Monitor }

proc p,; comienza

repite p;

critl; v;

por siempre termina

proc p*; comienza

repite p;

crit2; v;

por siempre termina

Es buen ejercicio el ver que esta soluciljn cumple con todos los requisitos de exclusi6n mutua, que no haremos aquí.

Esta implantación de los semaforos es la mas fuerte de todas las que hay debido a que el monitor se encarga de producir una cola (de procesos suspendidos, dotando así de justicia a los sem6foros de manera automatics.

SIMULACION DE UN MONITOR CON SEMAFOROS

De la misma manera que es posible el simular un sem6foro utilizando un monitor, es posible el simular un monitor por medio de los semgforos. De ksta manera se mostraría

La exclusión mutua es fscilmente simu- lada con el uso de un sem6foro binario. Todo proceso empezar5 con un wait y terminara con un signal a la misma variable, s. Para cada condición dentro del procedimiento se tomadn un par de variables que tengan, una la condi- ción que se esta evaluando y otra una cuenta de cu5ntos procesos est6n esperando de tal manera que la instrucci6n mon.wait(c) se codificaría:

cuentacond = cuentacond + 1; signal(s); wait(condmon); cuentacond = cuentacond - 1;

De esta manera liberamos la exclusión mutua de los procesos para que alguno entre a su sección crítica y por tanto pueda emitir un mon.signa1. Este quedaría codificado por:

si cuentacond > O entonces

otro signal(s) signal(condmon)

De esta manera, lo que suceder6 es que condsem bloquead a los procesos que lo llamen y sólo hasta que no quede ningún proceso esperando un signal de condsem podrh algún otro proceso entrar a su sección crítica.

Lo Único que no es posible definir en este algoritmo es la cola de procesos esperando entrar, ya que depende de la implantación que se haya hecho de los sem6foros.

Page 32: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

34

CAPITULO VI

PASO DE MENSAJES

Las regiones críticas y los monitores son extrapolaciones de los semaforos. Todos ellos proveen estructuras de control para acceder a variables comunes a todos los procesos. Los monitores son versiones centralizadas. Cada proceso accede a variables comunes y se asegura de que lo haga de manera única. Este requeri- miento resulta natural en una computadora o en un sistema con memoria común. ?Que pasa en un sistema distribuido? En 41 las computa- doras estan fisicamente dispersas y por lo tanto la memoria de cada nodo no tiene acceso sino muy limitado a la memoria de otro nodo. Esto dificulta mucho la implantación de monitores, regiones críticas y semAforos.

En un sistema distribuido tenemos conec- tadas a muchas computadoras independientes que mandan y reciben mensajes. Estos men- sajes son enviados y recibidos sin la menor sincronización. Lo que necesitamos es un protocolo que nos permita la sincronización de estos mensajes.

Una extrapolación de sem5foros permite un enfoque llamado message passing, (pase de mensajes), el cual puede ser visto como el extender los semAforos para implantar el mandar datos así como la sincronización. Cuando usamos el pase de mensajes para sincronización y comunicación, los procesos

Lo que Dios ha creado Cita biblica de Samuel Morse en

el primer mensaje telegrdfio

envían y reciben mensajes en vez de leer y escribir en variables compartidas. La comu- nicación es lograda por que un proceso, al recibir un mensaje, recibe tambien valores del proceso que se lo envió. La sincroni- zación se obtiene ya que el mensaje es recibido despues de haber sido enviado restringiendo la ocurrencia de estos dos sucesos.

Un mensaje es enviado al ejecutar

envía lista-expresiones a destino

El mensaje contiene los valores de las expresiones en lista expresiones en el momento que se ejecuta. Destino da al programador control sobre donde va el mensaje, y de aquí, que proceso puede recibir el mensaje. Un mensaje es recibido al hacer

-

recibe lista - variables de fuente

Donde lista variables es una lista de variables a donde ir5n a dar la lista expresiones vista anteriormente. La fuente permite decidir de donde ser6 reci- bido el mensaje así como que proceso gen- er6 la información solicitada. La recepción del mensaje efectúa la asignación de valores

Page 33: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

35

a las distintas variables y posteriormente destruye el mensaje permitiendo liberar jca- nales para la recepci6n de otro mensaje.

El diseño de primitivas para pase de men- sajes conlleva los problemas de la definición de la semAntica de las primitivas. ¿Como se definen hente y destino? Y también el problema de los protocolos de sincronización. Exist.en varias soluciones para estos problemas las cuales veremos a continuación.

Especificaci6n de Canales de Comunicaci6n

Tomados juntos Fuente y Destino de- finen un Canal de Comunicación. Necesita- mos el darle algún nombre a estos canales para poder saber de donde viene un mensaje y a donde van. Se han dado varias soluciones. La mas sencilla es el nombrar a los canales de comunicación del mismo nombre que los procesos que los utilizan. A esta estrategia se le llama Nombramiento Directo (Direct Nalm- ing). De tal manera que

envía ele - prod a consumidor

envía un mensaje que solo puede ser reci- bido por el proceso consumidor. De manera an5loga

recibe ele - prod de productor

permite recibir un mensaje enviado por el proceso productor.

El nombramiento directo es f5cil de i m - plantar y de usar. Hace posible a un proceso controlar el tiempo en el que recibe un men- saje de otro proceso. Utilizando este enfoque podemos escribir nuestro sistema de produc- toreskonsumidores como:

Programa Prod-Cons; proc Productor; comienza

repite produce(e1emento); envía elemento a Consumidor

por siempre termina

proc Consumidor; comienza

repite recibe elemento de Productor

consume(e1emento); por siempre

termina

comienza { Principal } comiencen productor; consumidor;

terminen termina

La solución es muy natural y sencilla de programar. Este modelo permite el encade- namiento de procesos, llamado tambikn pipe- line (tubería). Se tienen a varios procesos concurrentes en los que los datos de salida de uno sirven de datos de entrada a otro. Se le llama pipeline por que los datos fluyen como el agua de una tubería.

¿Qué sucede si tenemos a m5s de un proceso que produce o mAs de uno que con- sume? Tendríamos que enviar desde cada proceso productor o recibirla desde cada proceso consumidor. Pero sucede que el nombramiento directo solo nos permite reci- bir mensajes de un solo proceso. Supongamos que tenemos un manejador de impresora que recibe en forma de mensaje las peticiones de impresión. El manejador debería ser capaz de recibir mensajes de cualquier proceso. Por lo que no podríamos implantarlo usando nom- bramiento directo.

Page 34: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Pensemos en una tienda, hay llega cualquier cliente, algún proceso, el cual es atendido por un server, un proceso como el manejador de la impresora. A este esquema se le denomina cliente/servidor y es muy utili- zado dentro de la programación concurrente. Para el cual no sirve el enfoque de nombra- miento directo.

Podíamos pensar que cada cliente deja una nota donde especifica del trabajo que desea se le efectúe. Por otro lado habr4 un server que tomar4 las notas y las atender5 en el orden en el que llegaron. (Esto es utilizado por casi todas las torterías del D.F.). Definimos entonces un buz6n (mailbox) donde cada cliente deja su nota al server. Por desgracia, esto es bastante costoso de implantar si no se tiene una red especial de comunicaciones. Cuando un mensaje es enviado, tiene que esperar a que sea efectuado un recibe por parte del buzón. Desputs, se tiene que dar a conocer a todos los procesos que un nuevo mensaje ha llegado al buzón, y una vez que es tomado por alguien se tiene que notificar también.

Un tipo especial de buzones lo forman los denominados Puertos (Ports) en los cuales solo un proceso recibir5 los mensajes puestos en él. Existe una implantación de puertos donde al ser enviado un mensaje solo un proceso puede ser despertado por este hecho pero cualquier otro proceso puede acceder al puerto. Se asume que cada proceso ver4 si existe nueva información dentro de el puerto y tambitn cada proceso se hace cargo de tomar solo los mensajes que se le correspon- den. La implantación de los puertos no es tan costosa como la de un buzón. Esta también es flexible ya que se tiene un puerto asociado con cada proceso así como puertos del sistema bien definidos donde estan los mensajes dis- ponibles para todos los procesos.

Si la definición de los canales de comuni- cación es hecha al principio del sistema y son inamovibles se habla de un nombramiento

36 est4tico de canales. Si, por el contrario, estos se definen dentro del sistema puditndose dar de alta y baja a voluntad se le llama nombram- iento din5mico de canales.

Existen casos en los que es mejor el usar alguno de ellos en particular, por ejemplo, al manejar un sistema de archivos por medio de paso de mensajes ser4 mejor el tener un nombramiento din4mico de canales (los archivos). En el caso de UNIX que permite redireccionar la salida de un programa ten- dríamos un nombramiento est4tico.

Al final de estas notas se ver4 el caso par- ticular de una implantación de paso de men- sajes.

SINCRONIZACION

Otra de las propiedades importantes del paso de mensajes se da al decidir si su uso causa una demora. Una instrucción es no- bloquea (nonblocking) si su ejecución nunca demora al proceso que la utilizó; en otro caso se dice que es bloquea (blocking). En algunos esquemas de pase de mensajes, los mensajes son puestos en un almacenamiento temporal de tipo buffer mientras son recibidos. Una vez que se tiene lleno el almacenamiento al ejecutarse otro envía se puede demorar su ejecución o envía puede regresar un código al proceso que lo utilizó para decirle que el mensaje no puede ser mandado. De manera similar, en la ejecución de un recibe cuando no existen mensajes mandados puede esperar a que llegue uno o enviar un código de error que indique que no existe ningún mensaje disponible.

Si el sistema tiene un almacenamiento indefinido (o se utilizan estructuras de datos din4micas como colas, listas, etc.) los procesos nunca son detenidos al hacer un envía. A esta forma de enviar los mensajes se le denomina paso de mensajes asíncrona y envia-no-esperes

Page 35: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

37 Un recibe que bloquea es implícitamente

una forma de sincronización ya que el recep- tor es suspendido hasta que un mensaje es enviado. El implantar la sincronización con un recibe que no bloquea nos llevar5 a una espera activa. Dijkstra propuso un esquema de comunicación selectiva a traves de unos objetos llamados guarded commands que permiten lograr a los envíos y recepciones que no bloquean tengan el mismo significado sem5ntico que los que bloquean.

(send no-wait). El paso de mensajes de manera asíncrona permite el enviar mensajes por un proceso sin necesidad de que algún otro proceso los reciba. Esto puede llevar a un caso extremo de que los mensajes al ser recibidos por un proceso no reflejen la realidad. En el otro extremo si no almacenamos los mensajes catda vez que se efectúe un envía deber5 existir un recibe los cuales se ejecuten de manera se- cuencial. A esto se le denomina paso de men- sajes síncrono. Cuando se utiliza, un intler- cambio de mensajes representa un punto de sincronización de los procesos involucrados. A s í , el mensaje reflejar5 el estado actual de el proceso que lo envía. Es m&, cuando el envía termina, el proceso que envió puede hacer suposiciones del estado del proceso que lo recibió. Entre los dos extremos se encuentra el paso de mensajes con un bounded buffer.

El suspender a un proceso que efectúa un recibe es muy común ya que por lo general1 el proceso no tiene nada que hacer hasta que no le llegue el mensaje. Sin embargo, la mayoría de los lenguajes así como de los sistemas operativos tienen una versión no bloquea de esa instrucción. Esto le permite a un proceso el recibir todos los mensajes disponibles y elegir uno para procesarlo (o lo que es lo mismo planificarlos).

-

En algunos casos se permite un control m5s completo cambiando la instrucción a la forma.

recibe lista - var de fuente cuando B

Esta solo permite recibir mensajes 110s cuales hagan a B verdadera. Lo cual le per- mite al proceso “ver” el contenido de un mensaje antes de recibirlo. El hacerlo de manera explícita resuelve muchos problemas de siin- cronización aunque es claro que no es indis- pensable ya que se puede hacer una copia del mensaje para ver que tiene y si no es el adecuado volver a enviarlo a nosotros.

Un guarded command tiene la forma

guard -> instrucción

El guard consiste de una expresión booleana, a la que sigue opcionalmente una instrucción de paso de mensajes. El guard gana si la expresión booleana es verdadera y el ejecutar la instrucción de pase de mensajes no causa demora; el guard falla si la expresión booleana es falsa; el guard (temporalmente) ni gana ni pierde si la expresión es verdadera pero al ejecutar la instrucción de paso de mensajes no puede ejecutarse sin causar demora. De tal manera que la instrucción de alternativas

si G, -> S , G, -> S,

G - > S , fin b

es ejecutada como sigue. Si al menos un guard gana, uno de ellos, Gi, es seleccionado de manera no determinística; la instrucción de paso de mensajes de G, es ejecutada (si esta presente); entonces Si, la instrucción que sigue al guard es ejecutada. Si todos los guards fallan, el comando se aborta. Si todos los guardias ni ganan ni pierde la ejecución se suspende hasta que alguno de ellos gana. (Obviamente, el abrazo mortal es posible.) La ejecución de una instrucción iterativa es igual a la anterior, solo que la selección y ejecución

Page 36: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

de un guard command es repetida hasta que todos los guard fallan terminando el comando mas que abortar.

Para ejemplificar lo anterior utilizaremos un proceso almacCn que recibe la producción de un proceso productor y que sea tomada por un proceso consumidor.

proc almacCn var elementos[MAX-1] : espacio;

frente, atrh : entero; tamaño : entero;

comienza frente = atras = tamaño = O mientras verdad haz

comienza tamaño < MAX ;

recibe espacio[atrAs] de productor - > tamaño <- tamaño + 1 ; atras <- (atras + 1) mod MAX

tamaño > O ; envia espacio[frente] a consumidor ->

tamaño <- tamaño - 1; frente = (frente + 1) mod MAX

termina

proc productor comienza

repite produce(e1emento) envía elemento a almacén

por siempre termina

proc consumidor comienza

repite recibe elemento de almacén

consume(e1emento) por siempre

termina

comienza { Principal } comiencen productor; consumidor;

terminen termina

CONSTRUCCIONES DE ALTO NIVEL

38

Las primitivas vistas son suficientes para programar cualquier tipo de interaccih de procesos usando el paso de mensajes. Para programar interacciones del tipo cliente/servi- dor el cliente manda un envía seguido de un recibe, y el servidor un recibe seguido por un envía. Dado que este tipo de interacción es bastante común existen comandos de alto nivel que directamente permiten el uso de este tipo de construcciones. A estos se les denomina llamadas a procedimientos remo- tos (remote procedure call) teniendo este nombre ya que su aparición se dio en sistemas donde el cliente llamaba a un destino remoto para que lo atendiera.

Cuando estos comandos son utilizados, el cliente interactúa con el servidor por medio de una instrucción llama. Esta instrucción tiene una similar a la de algunos lenguajes secuenciales.

llama servidor(entrada; resultados)

Donde servidor es el nombre del canal de comunicación. Si se usa nombramiento di- recto, service denota al proceso que atiende al cliente; si usamos un puerto o un buzón, service puede nombrar al servicio que se desea. Una vez que se llama al servidor el cliente espera a que el servidor lo pueda atender así como también a que se le regresen sus resul- tados. Lo que resulta en envía seguido de un recibe.

Existen dos enfoques bhicos para especi- ficar a el servidor. En el primero, la llamada es

Page 37: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

39

una declaracih , como la de un procedimien- to en un lenguaje secuencial.

remoto procedimiento servidor (entra entrada; sale resultados) { Cuerpo del proceso }

termina

Tal procedimiento es obviamente un proceso. Una vez que esta definido el proceso espera la llamada de un cliente. Asigna los valores de entrada a una lista de variables locales, ejecuta el cuerpo del proceso y regresa los resultados en una especie de contesta mensaje. Hay que notar que a pesar de no haber entrada o resultados la sincronización se produce. Un procedimiento remoto puede implantarse como un proceso que esta en espera activa de datos para procesar, esquema en el cual cada llamada sería procesada de manera secuencial tal vez con algún orden de prioridad. Tambitn cada llamada puede crear nuevos procesos los cuales se ejecutasen de manera concurrente, significando que las dife- rentes instancias de un servidor debieran sin- cronizarse si utilizasen variables comunes.

En el segundo enfoque, el procedimiento remoto es un comando, el cual puede ser puesto en cualquier lugar donde vaya un comando. Tiene la forma siguiente:

acepta servidor(entra entrada; sale resultados)

-> { cuerpo del proceso }

La ejecución de este comando suspende al servidor hasta que un mensaje enviado por un llama ha llegado. Entonces el cuerpo es ejecutado, usando los valores mandados por el llama así como cualesquier otras accesibles al comando. Una vez que se han mandado los resultados el servidor continua la ejecución en el siguiente comando.

Cuando acepta es utilizado para especifi- car el lado del servidor, a la llamada al proce- dimiento remoto se le denomina r&zvouz (encuentro) por que el cliente y el servidor se encuentran” durante la duraci6n de la

ejecución del cuerpo del proceso del comando acepta. Una ventaja del rendezvous es que las llamadas del cliente son respondidas en el momento que quiera el servidor; los coman- dos acepta pueden ser entonces anidados, por ejemplo. Otra ventaja es que el servidor puede tener diferentes rutinas de servicio usando acepta con diferentes cuerpos del proceso, se podría ejecutar una inicializacidn en el primer acepta. La última y mPs importante ventaja que se obtiene es que el servidor puede pro- porcionar mPs de un servicio. Este enfoque es casi siempre combinado con comunicación selectiva para permitir al servidor esperar una de varias requisiciones de servicio. Esto se puede ver en nuestro proceso de almactn modificado.

6 6

proc almactn var elementos[MAX-11: espacio;

frente, atrPs : entero; tamaño: entero;

comienza frente = atrás = tamaño = O mientras verdad haz

comienza tamaño < MAX; acepta deposita(entra elemento : Verdad)

-> espacio[atrPs] <- elemento; tamaño <- tamaño + 1 ; atrPs <- (atrAs + 1 ) mod MAX

acepta obtén( sale elemento : Verdad) -> elemento <- espacio[frente]

tamaño > O;

tamaño <- tamaño - 1 frente = (frente + 1) mod MAX

termina termina

Page 38: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

40 El almacCn implanta dos operaciones: de- hay que fijarse que no se hace menci6n de los

posita yobtkn. La primera es invocada por un mensajes, esto se debe a que la llamada a cliente usando procedimientos remotos lleva implícitamente

asociados dos operaciones, una de recepcih y llama deposita(a1go) otra de envi6 de mensajes.

La segunda es utilizada al ejecutar No todos los problemas pueden ser mode- lados de manera adecuada por las llamadas a

llama obtCn(alg0) procedimientos remotos, pero representa un buen esquema para la creaci6n de procesos

Nótese que las operaciones son realizadas que tengan una estructura de cliente-servidor. en el almacCn de manera simetrica. TambiCn

Page 39: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

41

CAPITULO VI1 1 4 7 6 8 6

PROBLEMAS DE LA PROGRAMACION CONCURRENTE

La humanidad se impone solo problemas que pue& resolver Karl Mam

En la programación concurrente existen problemas asociados con ella que han sido bien documentados y se han dado estrategias generales para evitarlos. Estos problemas gen- eralmente se presentan en cierto tipo de prob'le- mas. Dentro de este capítulo veremos los problemas mAs comunes de la programaci'ón concurrente así como las estrategias generales que se han tomado para hacerles frente.

IMPLANTACION

El primer problema de la programaci6n concurrente es el de la implantación de mocle- los. Este problema no interfiere con los usua- rios de las soluciones ya terminadas, caso en el que nos encontramos la mayoría, pero es bueno verlo para darse una idea de El.

A lo largo de estas notas hemos visto esquemas enfoques y soluciones dentro de la programación concurrente. Dentro de ellas generalmente se han hecho pequeños comen- tarios sobre la facilidad o dificultad de la implantacih de estas soluciones. Aquí vere- mos los problemas generales que conlleva cualquier implantación.

EL primer problema con el que nos to- paremos es el de el sistema que vamos a

ocupar. Podemos querer trabajar concurren- cia en sistemas de tipo dedicado (PCs), de multiprocesadores (HP 9000, CDC 9600), o en sistemas distribuidos (Redes de computa- doras ya sean micros, minis o mainframes). El estar en cada una de estos sistemas ofrecer5 ventajas para ciertas soluciones y dificultades si no es que imposibilidad para otras. Un ejemplo de ello es el uso de variables globales para la creación de monitores y sem5foros. Dentro de sistemas dedicados o multiprocesa- dores se encuentra de manera natural la implantación de estas soluciones mientras que el hacerlo en un sistema distribuido es suma- mente costoso y dificil. Por otro lado el en- foque de paso de mensajes es el usado en sistemas distribuido pero no por ello deja de ser valido en alguno de los otros sistemas, este enfoque es usado en algunas micros.

Al hacer una implantación de algún modelo de programación concurrente se deben tomar en cuenta dos cuestiones primordi- almente: Costo y Facilidad.

En el costo tendremos en cuenta el tiempo del CPU que esta siendo desperdicio en over- head. Es decir, el tiempo que el CPU esta realizando alguna operación debida al proto- colo que se esta usando. Este tiempo no es utilizado para ejecutar instrucciones de alguno

Page 40: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

de los procesos y podríamos pensar en É1 como en una perdida necesaria. Por ejemplo, en el uso de semfiforos el suspender a un proceso se traduce en varias instrucciones en las que se le dice al sistema operativo que ese proceso no es elegible para darle tiempo de CPU asi como la actualización de la cola de procesos suspendidos por algún semfiforo. Dentro de costo tambien tendremos en cuenta el de comunicación que esta presente en los sistemas distribuidos. Si tenemos una implan- tación de un monitor en un sistema distribuido el realizar una actualización de alguna de las variables comunes se traducir4 en: enviar una señal a todos los nodos donde se anuncia el cambio sobre la variable común y cuando todos est4n de acuerdo enviar una copia de el cambio a todos los nodos del sistema. Esto, como se ve, podría ser sumamente costoso a la hora de pagar el teltfono. Lo que nunca se debe perder de vista es que no importando el costo que se tenga nuestro sistema debe de ser seguro y confiable. El tener a un nodo de un sistema distribuido como monitor al que se le den a su cargo las tareas del monitor puede resultar mfis barato que la implantación pro- puesta mfis arriba. Pero, si por algún motivo, se cae la linea de comunicación con este nodo todos los demh quedarían bloqueados sin poder hacer nada.

La otra cuestión es la facilidad que repre- senta la implantación. Es decir, cada implan- tación lleva asociado una diferente programa- ción de sistemas. Esta puede ser simple, como en el caso de semfiforos, o sumamente com- pleja, como en el caso de guards de las llama- das a procedimientos remotos.

Por lo general tendremos que llegar a un cosa intermedia entre el costo y la facilidad de implantación.

42 VALIDACION

Otro de los problemas importantes de la programacibn concurrente es la validación de un sistema. En un programa secuencia1 se tiene que podemos asegurar que un pro- grama no falla. En el caso de la programación concurrente no se puede hacer ya que existen un número infinito de formas en las que se ejecuten los procesos paralelos.

Una de las formas es el probar que un proceso no interfiere en ningún otro proceso y que sus puntos de sincronía no producen ningún problema. Cuando se tiene un sistema con un alto grado de cohesión esta solución puede ser muy útil. Cuando estamos trabajando en soluciones que tienen un alto número de variables globales es claro que de esta manera no nos aseguraremos de nada.

La solución mfis adoptada es la de pro- poner un esquema de axiomatización de a- cuerdo al enfoque que estemos utilizando. Despues usar la lógica para demostrar que alguna propiedad se cumple. Estos esquemas de axiomatización dependen del enfoque que se haga. Y pueden ser encontrados en la bibliografia.

Veamos un ejemplo. Deseamos probar la supervivencia de los procesos para la solución al problema de exclusión mutua con dos proce- sos usando semfiforos.

Programa Semfiforo. var S : semfiforo;

prw p,; comienza

repite wait( S ) ;

crit,; signal(s); por siempre

termina;

Page 41: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

43 Se ha dado últimamente un gran impulso

a la demostración de la correctez de los pro- gramas concurrentes. Ya que es un t6pico bastante dificil como poco documentado.

prw p,; comienza

repite wait@);

crit,; signal(s);

por siempre termina;

comienza { Principal 1 S = 1; comiencen

terminen p,; p,;

termina.

Se convierte en:

Teorema (PI desea entrar a crit,) implica eventualmente (PI entra crit])

Prueba por contradicci6n 1.- (PI desea entrar a crit,) implica que ((PI

entra crit,) 6 (PI es suspendida indefinida- mente por que s=O)

2.- (P, es suspendida indefinidamente por que s=O) implica (P, esta en crit,). Esto se debe a que s = l al principio y para que s==O entonces alguien debe estar es su seccilón crítica. Como no los esta PI entonces debe ser P, ya que solo son dos procesos.

3.- (P, esta en crit,) implica eventualmente (P, ejecuta signal(s)).

4.- (P, ejecuta signal(s)) y (PI esta indefinicla- mente suspendida por que s=O) implican (PI entra a crit,). Véase la definición ‘de semgforo.

5.- (PI desea entrar a crit,) implica eventu- almente (PI entra crit,) Hemos demostrado que lo contrario lleva

a una contradicción.

Como se ve este tipo de pruebas se har;in solo cuando en aquellas partes donde re- almente se necesiten ya que no es trivial la demostración.

SUPERVIVENCIA

Este problema lo vimos aparecer al ver la exclusi6n mutua. Se refiere a la posibilidad de que cualquier proceso pueda entrar a su secci6n crítica en un tiempo finito.

El hacer una soluci6n a este problema para el caso de dos semaforos la vimos en la sección anterior, así como su demostración. Para el caso de n procesos la soluci6n no es tan fk i l .

En ambiente mAs complejo este problema surge debido a la imposibilidad de un proceso de utilizar recursos que necesita. Esto puede deberse a que algún otro proceso haga un uso muy frecuente de ellos.

Pensemos en nuestro ejemplo de la carre- tera. 2Qu6 sucede si el semaforo se descom- pone ó si el coche que se encuentra cruzando el puente se detiene? Esto podría causar al coche que se encuentra esperando del otro lado el que estuviera mucho tiempo esperan- do, recordemos que el tiempo de computa- dora es muy caro, o que, peor aún, nunca pudiera pasar.

Por lo tanto es necesario el tomar en cuenta ese tipo de contingencias. Se debe tener alguna forma de poder saber si algún proceso se encuentra “congelado” y poder sacarlo del apuro o darlo de baja. Es necesario saber de manera aproximada el número de veces que un proceso determinado usa un recurso para impedir que lo use demasiado impidiendo a los otros procesos el que sobre- vivan.

Page 42: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

JUSTICIA 44

ABRAZO MORTAL

El problema de justicia esta íntimamente ligado con el de supervivencia. Ya que en un sistema donde no haya justicia es muy proba- ble el que se de el caso de que algún proceso se muera de inanición. Este es un problema muy serio dentro del area de la programación concurrente por que es muy subjetivo. Dentro de la definición de las tknicas de programa- ción concurrente no se habla de este problema y se le deja a quienes hagan la implantación el que vigilen que sea un sistema justo.

Cuando se da una cierta prioridad a los procesos es cuando mas riesgo se tiene de que el sistema no sea justo. Por lo que mucho se dice que un sistema debe ser “democra- tico”.

La estra- tegia general tomada para resolver este

Este es seguramente el problema m4s conocido de la programación concurrente. Existen casos documentados de las conse- cuencias catastróficas de este problema. Este se conocía a pequeña escala en algunos pro- gramas de programación concurrente. Fue hasta que se empezaron a utilizar sistemas a gran escala de tipo concurrente, cuando se vio su importancia. Por ejemplo, se habla de una línea area que sufrió perdidas por varios miles de dólares debido a que su programa de reser- vaciones entraba a un abrazo mortal con- gelando al sistema.

Figura 1 II I ’

problema es el de las colas. Por lo que en cada caso que necesitemos asignar un recurso lo haremos en base a una cola de procesos que lo quieran utilizar. Se puede tener una cola de procesos esperando entrar a su sección crítica ó usar una rutina del monitor. En el caso de paso de mensajes lo que se crean son colas de mensajes. De esta manera se puede propor- cionar justicia en el caso de un proceso este esperando respuesta a su mensaje. TambiCn se pueden mandar mensajes con un número de prioridad asociado para permitir al proceso receptor que pueda decidir a cual atender primero.

Este problema es responsabilidad del sis- tema y el que lo use deber5 ver que realmente existe.

P r imero veamos un ejemplo de 61. Si en nuestro coche viaja- mos en un tramo donde existen dos puentes “críti- cos” y hay mucho trafico puede llegarse

a una situación como la que se muestra en la figura 1.

Podemos observar que en ella ningún coche podr4 pasar ni de un lado ni del otro. Este problema se da cotidianamente en las grandes ciudades siendo la causa de muchos embotellamientos.

El problema en computación se da de la manera siguiente. El proceso Pi esta utilizando un recurso x y el proceso P. utiliza uno y. Si entonces Pi desea utilizar el recursoy esperara a que se desocupe. Y si P. desea utilizar x tendremos un abrazo mortai, ya que ninguno de los dos procesos podra continuar su ejecuci6n. Es claro que en el caso de dos procesos es fki l detectarlo y de alguna mane- ra resolverlo. Hacemos que cualquier de los

1

Page 43: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

45 caso de que tengamos la entrada de un proceso condicionada y que esta condici6n nunca se cumpla.

dos procesos deje su recurso para permitir al otro continuar.

En el caso de tener un sistema con muchos procesos ejecutandose concurrentemente no es sencillo el poder detectar este problema. !Sin embargo debido a su importancia se han estu- diado diferentes algoritmos para saber cuando se ha llegado a un estado de abrazo mortal. Esto se logra haciendo un estudio de graficas dirigidas donde cada nodo es un proceso clue apunta a otro nodo que es un recurso. El recurso a su vez apunta a aquellos procesos que lo solicitan. Si en la grafica existen ciclos tendremos un caso de abrazo mortal. Se han creado algoritmos que permiten el recuperar- se del estado de abrazo mortal. Si no se logra recuperar se reinicializa el sistema.

Este problema es tema de un gran estudio y debe de darsele primordial importancia al revisar el programa concurrente. Al estudiar en el siguiente capítulo los problemas clBsicos de la programación concurrente veremos clue es factible de darse en cualquier problema de concurrencia.

LOCKOUT

Volvamos al ejemplo antes señalado. Si tenemos semBforos que nos dan el paso en los cruces y ambos se ponen en rojo. Ningún coche podrB pasar a pesar de que ninguno esta en la secci6n crítica. Este problema es el de lockout. Se presenta generalmente en pro- gramas que tienen una sincronización de tipo condicionada.

Este se da en el paso de mensajes. En el caso de los comandos de tipo guard se puede dar que tengamos a procesos que ni ganen ni pierdan y por lo tanto nunca entren a su sección crítica o se puedan sincronizar. O bien en el caso de regiones críticas puede darse el

Este problema es generalmente causado por el programa. Hay que tener cuidado de cuidar esos casos donde el uso de recursos o la entrada a una sección crítica sea de tipo con- dicionado ya que seran fuente generadora de problemas.

CONCLUSIONES

El hacer uso de la programacibn concu- rrente permite el mejorar programas de todo tipo y es indispensable para la creaci6n de sistemas de computo. Es un tipo de programa- ción el cual es muy complejo y se debe tener mucho cuidado ya que cualquier error puede resultar muy co$to$o.

Dentro de este ambiente el probar que todos los procesos que forman parte del sis- tema no representara que el sistema sea con- fiable. Por lo que se debe probar de manera consistente antes de poder liberarlo.

Es necesario el documentarse acerca del sistema donde se va a implantar una soluci6n que utilice programación concurrente, así como del manejo de ella que se haga en el sistema. Esto es de vital importancia, ya que si todo sistema cae dentro de alguno de los enfoques que hemos visto, pero cada uno tendra algunas características que Eaciliten su uso o bien algunas restricciones. En la mayoría de los casos los errores surgen del desconocimiento del sis- tema o del compilador que se utilice. Finalmente se debe hacer especial incapie en la documen- tación que se haga de un sistema que use concurrencia ya que el darle mantenimiento, sin buena documentación, puede resultar prohibitivo o imposible.

Page 44: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

46

CAPITULO VI11

PROBLEMAS CLASICOS DE LA PROGRAMACION CONCURRENTE

Dentro de la programación concurrente existen problemas que son prototipo, cuyas soluciones sirven de base para la solución de problemas m5s generales. Estos problemas son denominados cl5sicos ya que son materia obligada para aquellas personas que intentan dar un nuevo enfoque a la programación con- currente o el proponer algún lenguaje que la maneje.

Estos problemas tienen varias soluciones dependiendo de que enfoque utilicemos. Los problemas se resolveran utilizando sem5foros y ademas con el enfoque que permita una solución mas simple.

Dentro de cada problema se explicar5 su importancia y a que tipo de problemas repre- senta.

PRODUCTORES/CONSUMIDRES

Este problema lo estuvimos utilizando para “probar” los diferentes tipos de enfoques que se usan en la programacih concurrente.

As writers become more numerous, it is natural for readers to become more indolent.

Oliver Goldsmith

o un archivo, los consumidores. Este problema surge ya que el proceso que produce por lo general lo hace a una velocidad mas rApida de lo que se puede consumir. De ahí el tener que guardarlo en un espacio temporal, del cual lo tome el consumidor. Vamos aquí solamente a reproducir la solución obtenida anteriormente y ver cómo funciona.

Programa Prod/Cons var espacio, mutex - prod, mutex cons,

elem :semAforo -

proc productor comienza

repite produce; wait(espaci0); wait(mutex - prod); almach[atrAs] = ele prod; atras = atras mod TAMAÑO + 1; signal(mutex prod); signal(e1em);

-

por siempre termina

Productores/consumidores es un problema típico de los sistemas operativos. En ellos exis- ten programas que generan datos, produc- tores, v los mandan a el usuario. la imnresora

Page 45: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

47 proc consumidor comienza

repite wait(elem)

wait(mutex cons) ele cons = almacén[frente] frente = frente mod TAMAÑO + 1; signal(mutex-cons) signal(espaci0)

consume por siempre

- -

termina

comienza { Principal } elem = O; espacio = TAMAÑO; mutex - cons = mutex prod = 1 ;

comiencen productor; consumidor

-

terminen termina

Tenemos dos variables de exclusión mutua las cuales previenen que dos productores entiran al almacén al mismo tiempo y lo mismo con los consumidores. Adem8s tenemos dos semaforos que nos hacen saber si el almacén tiene es- pacio donde meter la producción del produc- tor y si el almacCn tiene elementos que con- sumir.

La solución usando semaforos es bastante simple pero sin embargo no es tan clara co'mo la que se puede lograr otro enfoque.

Este problema es mucho m8s claro de re- solver con el uso de paso de mensajes, ya que cualquier proceso que produzca algo se lo manda directamente al proceso, o procesos usando un buzón, que lo vayan a consumir.

proc almacén var elementos[MAX- 11: espacio;

frente, atr8s: entero; tamaño: entero;

comienza frente <- atr8s < - tamaño < - O; mientras verdad haz

comienza tamaño < MAX ; recibe espacio[atrAs] de productor ->

tamaño <- tamaño + 1 ; atr8s <- (at& + 1) mod MAX;

tamaño > O ; envía espacio[frente] a consumidor ->

tamaño <- tamaño - 1; frente <- (frente + 1) mod MAX;

termina termina

proc productor comienza

repite produce(e1emento) envía elemento a almacén

por siempre termina

proc consumidor comienza

repite recibe elemento de almacén

consume(e1emento) por siempre

termina

comienza { Principal } comiencen

productor; consumidor;

terminen termina

Esta solución es la que se logra mediante el uso de comandos guardados en la cual tene- mos a nuestro almacén que recibe los elemen- tos producidos solo cuando tiene espacio y los envía cuando hay elementos en él.

Page 46: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Esta solución tiene la ventaja de su clari- dad. El problema, de hecho, es mas facil de resolver utilizando cualquiera de los enfoques del paso de mensajes ya que realmente lo que se tiene es a un proceso, el productor, envi- ando un mensaje, que bien puede ser cualquier cosa, a un proceso receptor, el consumidor. Por lo que resultan mas naturales las solu- ciones que se consiguen utilizando este en- foque.

LECTORES/ESCRITORES

Este problema es la abstracción de las op- eraciones que realizan las bases de datos dis- tribuidas. En ellas se puede tener a varios procesos leyendo datos de manera no ex- cluyente. Y son mutuamente excluyentes al escribir datos, para evitar la inconsistencia de los datos.

Este problema fue resuelto usando semgforos por [COURTOIS, NYWS Y AS, 1971 J Los cuales dieron las siguientes soluciones.

Programa lectores/escritores, var nlec: entero; { Número de lectores }

recurso, revisa: semaforo;

proc lector comienza

repite wait(revisa); nlec = nlec + 1 ; si nlec = 1 entonces wait(recurs0) signal(revisa) { Lee > wait(revisa) nlec = nlec - 1 si nlec = O entonces signal(recurs0) signal(revisa) por siempre

48 proc escritor comienza

repite wait(recurs0) { Escribe } signal(recurs0)

por siempre termina

comienza { Principal } comiencen

lector, lector, ..., lector escritor, escritor, ..., escritor

terminen termina

Dentro de esta solución se utiliza el semaforo de recurso para lograr que entre un escritor solo sin nadie. Y el semaforo revisa que es utilizado para que los lectores les avisen a los escritores que estan haciendo uso del recurso.

Solo se verifica el semaforo recurso cuando quiere entrar el primer escritor y una vez que ya esta adentro todos los demds entran sin preguntar nada.

Esta solución es injusta ya que le da ventaja a los lectores para utilizar el recurso. En la solución no asegura que un escritor pueda entrar a su sección crítica, ya que puede darse el caso de que haya un lector adentro y siga llegando una corriente ininterrumpida de ellos de tal manera que siempre haya mas de uno.

La segunda versión de la solución de este problema le da mayor prioridad a los escri- tores. Si algún escritor desea hacerlo, lo hara tan pronto como sea posible, esto se lograra evitando que entren mas lectores a partir del momento de que un escritor quiera escribir. De esta manera el escritor solo tendra que esperar a que los lectores terminen.

Page 47: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

49 programa lectores/escritores2 En esta solución se utilizan cinco semsoros var nlec, nesc: entero; { Número de lectores } y dos contadores auxiliares. El resultado es

recesc, reclec, revl, rev2 ,rev3: sem5foro; bastante complejo. El uso de tantos sem5foros

proc lector comienza

repite wait(rev3); wait(rec1ec); wait(rev1); nlec = nlec + 1 ; si nlec= 1 entonces wait(recesc); signal( rev 1 ); signal(rec1ec); signal(rev3); { Lee 1 wait(rev1); nlec = nlec - 1 ; si nlec = O entonces signal(recesc) signal(rev1)

por siempre termina

proc escritor comienza

repite wait(rev2); nesc = nesc + 1 ; si nesc = 1 entonces wait(reclec) signal(rev2) wait(recesc) { Escribe } signal(recesc) wait(rev2)

nesc = nesc - 1 si nesc = O entonces signal(rec1ec) signal(rev2) por siempre

termina

comienza { Principal } comiencen

lector, lector, ..., lector escritor, escritor, ..., escritor

terminen termina

se da de la siguiente manera:

recesc - exclusicjn mutua de los escritores. reclec - excluye a los lectores cuando los

escritores esperan el recurso o lo estiin utilizando.

revl - asegura exclusi6n mutua al cambiar “nlec”.

rev2 - asegura exclusión mutua entre escri- tores al cambiar “nesc”.

rev3 - da mayor prioridad a los escritores deteniendo a los lectores que lleguen después de un escritor.

Esta solución nos lleva de un extremo a otro. De haber tenido problemas de super- vivencia con los escritores en la primera solu- ción ahora tenemos que los que pueden no sobrevivir, o “morir de hambre”, son los lec- tores.

La solución dada por los semAforos a este problema es bastante regular ya que no nos permite asegurar nada respecto a la super- vivencia de procesos.

Un enfoque que podría servir es el de los monitores ya que el monitor puede servir de Arbitro para decidir quien hace uso del re- curso. La solución usando monitores es la siguiente.

programa lectores/escritores/mon monitor lee - esc; var nlec: entero;

escribiendo: entero ok-lee, ok-esc: condiciones

proc empieza a-leer comienza si escribiendo o no - vacío(ok esc) entonces mon.wait(ok-lee) nlec = nlec + 1 mon.signaI(ok-lee)

termina

-

-

Page 48: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

proc termina-leer comienza nlec = nlec - 1 si nlec = O entonces mon.signal(ok-esc)

termina

proc comienza - esc comienza si nesc < > O o escribiendo entonces mon.wait(ok-esc)

escribiendo = verdad termina

proc termina esc comienza escribiendo = falso si no - vacío(ok - lee)

entonces mon.signal(ok - lee) otro mon.signal(ok - esc)

-

termina comienza { Monitor }

nlec = O escribiendo = falso

termina

proc lector comienza

repite empieza a-leer { Lee 1-

termina - leer por siempre

termina

proc escritor comienza

repite empieza esc { escribe-} termina - esc

por siempre termina

comienza { principal } comiencen

lector, lector, ..., lector escritor, escritor, ..., escritor

terminen termina

50

Como se ve esta solución es mas clara que la de los semaforos. En ellas se utilizan a procedimientos definidos dentro del monitor para decidir a quien le toca entrar las reglas que se siguen son:

1.- Si hay escritores esperando entonces cualquier lector que llegue tendra que esperar a que terminen los escritores.

2.- Si hay lectores esperando a que termine un escritor, tendran prioridad sobre la próxima escritura.

Esta solución cumple la supervivencia ya que todo lector y escritor eventualmente en- trara a leer o escribir.

CINCO FILOSOFOS

El problema de los cinco filósofos es de gran importancia dentro de la programación concurrente. Ya que permite revisar los con- ceptos mas importantes de la programación concurrente en una situación muy grafica. Es un reto a todo aquel que proponga nuevas primitivas de programación concurrente.

El problema es así: En un monasterio viven cinco monjes que se dedican a la filosofia. Cada filósofo estaría contento si solo pensara y de vez en cuando comiera. A s í que su vida se reduce a un ciclo infinito de pensar y comer.

La forma en que esta arreglada la mesa para comer esta mostrada en la Figura 2.

En el centro de la mesa hay un tazón con espagueti que esta siempre lleno. Hay cinco platos y cinco tenedores. Un filósofo que desea

Page 49: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

51 comer entra al comedor, toma un asiento, comienza { Principal } come y regresa a su celda a pensar. Sin em- para número = O a 4 haz bargo, el espagueti esta tan revuelto que se tenedor[número] = 1; necesitan dos tenedores para poder come]-lo. comiencen (Se ha dicho que este esquema es un poco filósofo(o); forzado y que sería mas natural si se hablarh filósofo( 1); de un tazón con arroz y un par de palillos.) filósofo(2);

filósofo(3); El problema es proponer un ritual (proto- filósofo(4);

colo) que permita a los fil6sofos comer. Cada terminen filósofo puede usar los tenedores que se en- termina. cuentran al lado de debe satisfacer las condiciones de ex- clusión mutua, un tenedor no puede ser usado por dos filósofos al mismo tiempo; superviven- cia, que todo filósofo pueda comer; así como la ausencia de abrazo mortal, que tambikn llevaría a la muerte por inani- ción a los filósofos.

su plato. Este -protocolo

II Cinco Filosofor

Veamos pues una solución utilizando semAforos:

Programa filosofos,; var tenedor[4]: semhforos;

número: número;

proc filósofo(x); comienza

repite piensa; wait(tenedor[x]);

wait(tenedor[x+ 1 mod 51); come;

signal(tenedor[x]); signal(tenedor[x+ 1 mod 51); por siempre

termina;

Figura 2.

1476686

Como se ve la solución es muy simple y se basa en 5 sem5foros bina- rios, uno por cada tenedor. De tal manera que el tene- dor tiene dos esta- dos, en uso y libre, y si esta en uso uno espera a que se libe- re y una vez ter- minada la comida liberamos a los dos tenedores que tomamos. Veamos

si esta solución cumple los requisitos. Asegura exclusión mutua gracias a los semáforos, la su- pervivencia como siempre depende en mucho de la implantación de los sem5foros. El peligro de los abrazos mortales no es resuelto por este programa por lo que no sirve la solución. Si numeramos los tenedores en el sentido de las manecillas del reloj, el menor estar5 a la izquierda y ese será el que agarra primero el filósofo. Si llegan juntos los 5 filósofos a comer y toman los tenedores al mismo tiempo, nadie soltará el tenedor esperando que el de la izquierda lo haga que a su vez estar5 esperan- do al de la izquierda y así. Nadie soltarA el tenedor por lo que habremos llegado a una condici6n de abrazo mortal.

Page 50: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

programa filósofos; monitor mon-tenedores; var tenedor[4]: entero;

ok - come[4]: condición;

proc tomatenedor(i:entero); comienza si tenedor[i] 4 2 entonces

wait(ok come[i]); tenedor[ i 1 mod 51 = tenedor[¡+ 1 mod 51 - 1 ; tenedor[i-1 mod 51 = tenedor [i-1 mod 51 - 1 ; termina;

proc dejatenedor(i:entero); comienza

tenedor[i+ 1 mod 51 = tenedor[i+ 1 mod 51 + 1 ; tenedor[i-1 mod 51 = tenedor [i-1 mod 51 + 1 ; si tenedor[i + 1 mod 51 = 2 entonces

si tenedor[i-1 mod 51 = 2 entonces signal(ok-come[i+ 1 mod 51)

signal(ok come[i-1 mod 51) termina

-

comienza { Monitor }

termina Para i = O a 4 haz tenedor[i] = 2

proc filósofo(i:entero) comienza

repite piensa;

tomatenedor(i); come;

dejatenedor(i) por siempre

termina

comienza { Principal } Para número = O a 4 haz

tenedor[número] = 1 ; comiencen filósofo(o); filósofo( 1); filósofo(2);

filósofo(3); filósofo(4);

terminen termina.

52

La solución anterior no servía por que dej5bamos a los filósofos tomar los tenedores sin fijarse si alguien podría estar esper5ndolo. En esta solución hacemos uso de un monitor que permite el controlar cuando alguien va a tomar un tenedor o a dejarlo. Solo si los dos tenedores e s t h disponibles el filósofo puede tomarlos. Dado que es imposible el que todos los filósofos tomen un tenedor sin que este libre el otro nos aseguramos de que no se den abrazos mortales. Al utilizar una estructura de monitores no tenemos problemas de exclu- sión mutua. <Qué pasa con la supervivencia? Dado que estamos esperando a que los dos tenedores estén libres dos filósofos podrían causar a un tercero su muerte por inanición. Fijémonos en la solución. Si un filósofo toma sus tenedores impedir5 a los de los lados que lo hagan. Si dos filósofos se ponen a comer existir5 alguien cuyos tenedores los tengan aquellos que estan comiendo. Si estos dos filósofos piensan poco y comen mucho, o se turnan para pensar y comer, no dejar5n comer al otro, el cual morirA de hambre. Por lo que esta solución no es la adecuada.

El problema tiene una solución simple ?Qué pasa si restringimos a cuatro el número de filósofos? Al haber 5 tenedores uno de los filósofos podr5 tomar dos para comer. Al salir este podr5 entrar otro filósofo a comer habi- endo solo cuatro adentro uno de ellos podr5 comer y así. Al terminar de comer alguien podemos dar un tiempo para permitirle “salir” del cuarto y que alguien m5s empiece a comer antes de dejar a otro entrar al cuarto. De esta manera aseguraremos la “rotación” de los filósofos y aseguraremos la supervivencia de todos.

Una solución que permita a los cinco fil- ósofos estar en el mismo cuarto es aún tema de estudio.

Page 51: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

53

CAPI'TULO IX

LENGUAJES CONCURRENTES

A thing well said will be wit in all languages John D r y a h

A pesar de la aparición de artículos y libros estudiando a la programación concu- rrente los lenguajes concurrentes todavía no estan muy difundidos. Existen pocos de tnpo comercial y no estan disponibles en Mexico. Se han hecho adaptaciones aquí de lenguajes ya existentes y por lo tanto el resultado es limita- do.

Casi todos los lenguajes concurrentes que existen han sido desarrollados por equipos de investigación para llenar necesidades especifi- cas. En este capitulo mencionaremos los len- guajes concurrentes mas importantes. Ya sea por su divulgación o por su importancia histórica. Estos se dividen en diferentes tipos debido al enfoque que tienen. Los aquí pre- sentados son los m4s representativos de cada clase habiendo algunos otros que hacen uso de algunas cosas de un tipo y otras de otro. Finalmente se pasará revista a las facilidades que dan los microprocesadores para la imple- mentación de concurrencia.

LENGUAJES ORIENTADOS A PROCEDIMIENTOS

En estos lenguajes la interacción de proce- sos esta basada en las variables compartidas. (Debido a que los lenguajes basados en moni- tores son los mas conocidos de esta clase, se les llama frecuentemente modelo de monitor). Estos lenguajes contienen tanto objetos acti- vos (procesos) como objetos pasivos, compar- tidos (módulos, monitores, etc.). Los objetos pasivos son representados usualmente por variables compartidas, con procesos que im- plantan las operaciones en los objetos. Los procesos acceden a los objetos que requieren de manera compartida. Ya que a los objetos pasivos los acceden de manera compartida son sujetos de concurrencia. Por lo que los len- guajes son muy enfocados al asegurar la ex- clusión mutua.

Pascal Concurrente [Brinch Hansen 1975,19771

Este fue el primer lenguaje en basarse en monitores, de ahí que proveyera una manera de evaluarlos como componentes estructu- rales del sistema. Este lenguaje ha sido utili- zado para la creación de varios sistemas opera- tivos como Solo [BRINCH &SEN 1976~, 1977B], un sistema operativo para un solo usuario; Job

Page 52: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Stream, un sistema operativo por lotes para procesar programas en Pascal, y un sistema de control de procesos en tiempo real [BRINCH HANSEN 1977)

Uno de los mayores propósitos de Pascal Concurrente era el de asegurar que los pro- gramas se comportaran de manera reproduc- ible en su ejecución. Los monitores aseguran que rutinas que comparten datos no sean ejecutadas a la vez, por lo que el compilador generaba el código necesario para proveer la exclusión mutua.

Este lenguaje da al programador una m4quina abstracta eficiente, eliminando el codificar a lenguaje m4quina. Un lenguaje de programación de sistemas necesita permitir facilidades de acceso a perifkricos E/S y otros recursos de Hardware. Estos son vistos en Pascal concurrente como monitores implan- tados directamente en el Hardware. Para realizar una E/S el monitor correspondiente es llamado; la llamada regresa cuando la E/S ha finalizado. A s í que la noción de inter- rupción es eliminada al implantarse la E/S de manera síncrona.

Varios aspectos de este lenguaje han sido estudiados, incluyendo su enfoque de E/S, por [LOEHR 19771 [SILBERSCHATZ 19773 y [KEEDY 19791

MODULA [Wirth 19771

Modula fue desarrollado para computa- doras pequeñas, dedicadas, incluyendo apli- caciones de control de procesos. El lenguaje ésta basado bastante en PASCAL e incluye procesos, módulos de interfaz, que son como monitores, y módulos de perifkricos, que permiten módulos para programación de manejadores de periféricos.

El núcleo de tiempo de ejecución es pequeño y eficiente. El núcleo para una PDP 11/45 requiere solo 98 palabras de almace-

54 namiento y es extremadamente r4pido. No parte el tiempo del procesador entre los proce- sos como lo hace Pascal Concurrente. En cambio, ciertas operaciones - como wait - hacen que el procesador cambie de estado (el programador debe estar al tanto de ello y diseñar los programas acorde a esto ). Esto es a la vez una debilidad y una fuerza de Modula. Un pequeño y eficiente núcleo, donde el pro- gramador tiene control sobre el cambio de procesos, permite a Modula el ser usado para aplicaciones de control de procesos. Desafor- tunadamente, para poder tener tal núcleo, algunas de las construcciones del lenguaje - especialmente aquellas que tienen que ver con el control de la multiprogramación - tienen asociadas restricciones que solo pueden ser comprendidas en términos de la implantación del núcleo. Una variedad de sutiles interac- ciones entre las varias construcciones de sin- cronización deben conocerse para poder pro- gramar en modula sin recibir desagradables sorpresas. Algunas de ellas son descritas por [BERNSTAIN Y ENSOR 1981 1

Modula implanta una mAquina abstracta que éSta bien provista para manejar interrup- ciones y periféricos en los procesadores de la PDP- 1 l. Al contrario de Pascal Concurrente, donde el núcleo de tiempo de ejecución maneja interrupciones y E/S. Modula deja el manejo de ellos para el programador. De tal manera que nuevos periféricos puedan ser agregados sin modificar el núcleo. Todas las operaciones de E/S son manejadas con una instrucción doio (que es como un wait, excepto que es de- tenida hasta que se recibe la interrupción del periférico al que se invoco). Los domicilios de las interrupciones de E/S le son dadas al compilador al principio del modulo para poder hacer las ligaciones convenientes.

Un tercer aspecto nuevo de Modula es su habilidad para que variables declaradas en un modulo sean exportadas a otro. Las variables así tratadas pueden ser referenciadas m4s no modificadas fuera del alcance del modulo de

Page 53: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

interfaz. Esta es un arma de dos filos que puede llevar a dificultades si el programador no tiene cuidado con ellas. Mas, cuando son usadas selectivamente pueden incrementar la eficiencia de programas que accedan a esas variables.

En suma, Modula esta menos constreñido que Pascal Concurrente, pero requiere de mas cuidado al programar. Sus debilidades y fortalezas han sido evaluadas por [ANDKEWS

19811. Wirth, el diseñador de Modula ha desar- rollado Modula-2 [WIRTH, 19821 . Modula-2 re- tiene la estructura bAsica modular, pero of- rece mAs facilidades para la programación concurrente que no tienen semhticas menos sutiles. En particular Modula-2, provee coruti- nas a por tanto transferencia explícita de control entre procesos. Usando esto, el programador crea ayuda para la sincronización y la exclu- sión, como lo requiera. En particular, el pro- gramador puede crear módulos tipo monitor.

19791, [HOLDEN Y WAND 19791, y [BERNSMN Y ENISOR

Existen otros lenguajes de este tipo como Mesa y Edison. Los cuales pueden ser consultados en la bibliografia.

LENGUAJES ORIENTADOS DE MENSAJES

Los lenguajes orientados a mensajes y los orientados a operaciones estan basados en el paso de mensajes. Pero cada uno tiene una manera diferente de ver la interacción de procesos. En los lenguajes orientados a men- sajes las instrucciones send y receive son las usadas primordialmente para la interacción de procesos. A diferencia de los orientados a procedimientos no existen objetos comparti- dos de tipo pasivo, por lo que los procesos no pueden acceder directamente a todos los ob- jetos. En vez de ello cada objetos manejado por su cuidador, el cual realiza todas las opera- ciones sobre 61. Cuando una operación se va a realizar sobre un objeto se envía un mensaje a su cuidador, el cual realiza la operación y

55 regresa un mensaje de terminacih o tal vez de error. A s í , los objetos nunca son sujetos a concurrencia. CSP, Gypsy y PLITS son algunos ejemplos de este tipo de lenguajes.

Communicating Sequential Processes (CSP) [Hoare, 19781

Este lenguaje es importante ya que los conceptos utilizados en 61 han influido gran- demente en los trabajos subsecuentes de diseño de lenguajes concurrentes así como el diseño de programas distribuidos.

En CSP los procesos estan denotados por una variante del cobegin. Los procesos pueden compartir variables que solo se leen, pero usa comandos input/output para sincronización y comunicaciones. Se nombra a los canales de comunicación de manera estatica y directa y el paso de mensajes es síncrono.

El comando output en CSP tiene la forma

donde destino es el nombre de un proceso y expresión es un valor simple o estructurado.

destino ! expresión

Un comando input tiene la forma

donde fuente es el nombre de un proceso y objetivo es una variable simple o una vari- able estructurada local al proceso que con- tiene el input.

fuente ! objetivo

Los comandos Pr ! expresión

en el proceso Ps y Ps ! objetivo

en el proceso Pr concuerdan si objetivo y expresión tienen el mismo tipo. Dos procesos se comunican si ejecutan un par de comandos inpurloutput que concuerden. El resultado de la comunicación es que el valor de la expre- sión sea asignado a la variable objetivo; ambos procesos se ejecutan independientemente y de manera concurrente.

Page 54: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

Combinando comandos de comunicación con comandos de selección e iteración, el CSP provee un poderoso mecanismo para la pro- gramación de procesos que interactúen entre sí. Su fuerza radica en la idea sencilla que esta cuidadosamente integrada con otros pocos mecanismos. CSP no es un lenguaje com- pletamente concurrente, ni lo pretende. Por ejemplo, nombramiento estatico es algunas veces complicado de usar. Afortunadamente, esta deficiencia puede ser resuelta usando puertos. El cómo hacerlo es discutido breve- mente por Hoare y a detalle por Kieburtz y Silberschatz. Recientemente, dos lenguajes basados en CSP han sido descritos.

Gypsy [Good et a1.,1979]

Este es uno de los primeros lenguajes de alto nivel basados en el pase de mensajes, usa nombres de cajas de correo y paso de mensajes con buffer. Una de las atenciones de Gypsy era el construir sistemas verificables. Ha sido utili- zado para sistemas de propósito especial para arquitecturas de un solo o múltiples procesa- dores.

PLITS [Feldman 19791.

PLITS es el acrónimo de “Programing Language In The Sky” (Lenguaje de pro- gramación en el cielo) y fue desarrollado en la Universidad de Rochester. El diseño de PLITS esta basado en la premisa de que hay dificul- tades inherentes al combinar un alto grado de paralelismo con variables compartidas y por lo tanto el paso de mensajes es la manera apro- piada de la interacción de procesos en un sistema distribuido. Parte de un proyecto de investigacidn en diseño de lenguajes y compu- tación distribuida, PLITS esta siendo usado para programas de aplicación que se ejecutan en la red RIG (Rochester Intelligent Gate- way).

56 Un programa en PLITS consiste de un

número de módulos; los módulos activos son los procesos. El paso de mensajes es el Único medio para la interacción de módulos. Para no restringir el paralelismo, el paso de mensajes es asíncrono. Un módulo manda un mensaje que contiene los valores de algunas expre- siones a un módulo nommod ejecutando

send expresiones to nommod [about llave]

la última parte es opcional. Si se incluye, pega un identificador de transacción llave a el mensaje. Esta llave puede ser usada para identificar al mensaje de manera única o puede pegarse a diferentes mensajes para agrupar- los.

Un módulo recibe un mensaje ejecutando receive variables [from nommod] [about

llave]

Si las dos últimas frases se omiten, la ejecución del receive se retardara la ejecución del m6dulo hasta que llegue algún mensaje. Si se agrega el from, lo harA hasta que se reciba un mensaje de nommod. Y, finalmente, si se incluye about, el módulo esperara hasta que un mensaje con la llave adecuada.

El manejo adecuado de estas variantes permiten a PLITS flexibilidad a el programa- dor en el manejo de las comunicaciones. El uso de llaves, bien usado, puede emular a condicionales que se dan en otros lenguajes. Ya que permite al receptor el seleccionar el tipo de mensajes a recibir.

En PLITS, el recibir mensajes puede blo- quear la ejecución del módulo. Por ello PLITS tiene primitivas para verificar los mensajes que se encuentran disponibles para recibir, lo que permite al proceso evitar el bloqueo cuando no hay un mensaje disponible.

Los programas en PLITS ofrecen una interfaz para los procesadores que utilizan a

Page 55: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

RIG. Cada sistema huésped provee acceso a periféricos, un sistema de archivos, y un con- trol de tareas. Un núcleo de comunicacio~nes es lo que se requiere para intercomunicación de procesadores.

LENGUAJES ORIENTADOS A OPERACIONES

Estos lenguajes tienen a las llamadas a procedimientos remotos como medio primario de interacción de procesos. Estos lenguajes combinan aspectos de los vistos anteriormente. Como en un message-oriented, cada objeto tiene un cuidador asociado a 61; y como en un procedure-oriented las operaciones son hechas sobre un objeto llamado procedimiento. La diferencia es que quien llama a una operación y el cuidador que la implanta se sincroni:zan

la operación se ejecuta. Una vez termin mientr#- da ambos continúan asíncronamente. Distributed Processes, StarMod, Ada y SR :son algunos lenguajes de este tipo.

Ada [Departamento de Defensa de los E. U. ti.,

19811

Ada es un lenguaje creado para la pro- gramación de sistemas integrados, de tiempo real, controladores de procesos. Como los usados en barcos, aviones y demh equipo, de guerra. Por ello Ada ofrece ventajas para multiproceso y control de periféricos. Con respecto a la programación concurrente, la innovación de Ada es el concepto de ‘rendlez- vous’ para llamadas de procedimientos rerno- tos.

Los procesos en Ada son llamadas tareas. Una tarea es activada cuando un bloque que contiene su declaración es alcanzado. Las tareas deben estar anidadas y pueden interactuar con variables compartidas declaradas dentro de bloques que contengan a las tareas.

57 El mecanismo primario para la interac-

ción de procesos es la llamada de procedim- iento remoto. A estas se les llama entry; son puertos dentro de un proceso server especifi- cado a través de un comando accept, que se discuti6 en el Capitulo VII.

Desde su concepción Ada ha generado mucha controversia, mucha de la cual no es debida a la concurrencia. Sin embargo, pocas aplicaciones usando características de pro- gramación concurrente han sido hechas. Existen varios escritos donde se hacen com- paraciones de Ada con otros lenguajes que usan el mismo enfoque de concurrencia. Welsh y Lister lo hacen con CSP, Ada y Distributed Process; Wegner y Smolka comparan CSP, Ada y los monitores.

SR (Synchronizing Resources) [Andrews, 198 1 , 19821

SR como Ada, utiliza al rendezvous como forma de llamadas remotas. Pero, existen notables diferencias dentro de los lenguajes, como se describe abajo. Un compilador de SR ha sido implantado en la PDP-11 y fue usado para construir una sistema operativo para redes tipo UNIX.

Un programa de SR consiste de uno o m& recursos. La construcción de recursos contienen control de interacción de procesos y abstracción de datos. (En cambio Ada tiene una construcción para cada cosa: la tarea y el paquete). Los recursos contienen uno o m& procesos. Los procesos interactúan usando operaciones, las cuales son similares a las entrys de Ada. TambiénJos procesos de un mismo recurso pueden interactuar por medio de variables compartidas.

A diferencia de Ada, las operaciones pueden ser invocadas por dos instrucciones send, que bloquea al procesos, y call, que no lo hace. (El server que implanta una operación

Page 56: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

puede requerir forma particular de invoca- ci6n, si es necesario.) A s í tanto paso de men- sajes asíncrona como llamadas remotas son soportadas. Las operaciones pueden nombrarse de manera est5tica en el texto del programa o dinamicamente por medio de variables cuyos valores son los nombres de las operaciones. Un proceso puede por tanto tener un juego cambiante de canales de comunicaci6n.

En SR existen comandos de tipo guard con semantics similar a la de Ada. Existen pro- cedimientos, los cuales, a diferencia de los procesos, se ejecutan de manera repetitiva y secuencial.

Al soporte de los periféricos de control, SR provee una variante de los recursos llama- dos recursos reales. Un recurso real es similar a los módulos de periféricos de Modula; con- tiene manejadores de periféricos que per- miten unir variables a domicilios de registros de periféricos. Se pueden manejar vectores de interrupción. Una interrupción del Hardware es tratada como un send; las interrupciones como comandos in.

Distributed Processes (DP) [Brinch Hansen, 19781

DP fue el primero basado en llamadas remotas. Puede ser visto como un lenguaje que implanta monitores por medio de proce- sos activos mas que de procesos suspendidos. DP puede ser considerado como antecedente directo de Ada.

StarMod [Cook, 19801

Este lenguaje sintetiza aspectos de Modu- la y DP; toma ideas de modularización de Modula e ideas de comunicación de DP. Un módulo contiene uno o m5s procesos y, de manera opcional, variables compartidas por esos procesos. La sincronización dentro de los

58 módulos es dada por semPforos. Los procesos en diferentes m6dulos interactúan por medio de llamadas remotas; StarMod provee tanto procedimientos como rendezvous para imple- mentar el lado del server.

Argus [Liskov y Sheifler, 19821

También toma ideas de DP -procedi- mientos remotos implantados por procesos creados dinamicamente, que se sincronizan usando regiones críticas- pero va mPs all& Tiene bastante soporte a transacciones atbmi- cas. El lenguaje también incluye manejo de excepciones y mecanismos de recuperacibn, los cuales son invocados si ocurre una falla durante la ejecución de las operaciones atómi- cas. Argus esta a un m5s alto nivel que otros lenguajes vistos aquí en el sentido de que proporciona m5s sem5ntica a las llamadas remotas.

FACILIDADES DEL HARDWARE

Como ya se mencionó fue en las compu- tadoras IBM donde primero se dieron facili- dades para la concurrencia. La instrucci6n TST (test and set) fue la primera que se dio para el manejo de concurrencia a nivel de ensamblador. Sin embargo, fue poco lo que se desarrollo después de ello. Hasta hace poco se dan avances en este sentido los cuales revis- amos brevemente aquí.

El problema con el cual se han topado los diseñadores es el de la brecha conceptual. Esta se debe a que los programadores tienden a pensar a nivel de LAN (lenguaje de alto nivel). La arquitectura del hardware est5 basada en conceptos completamente distintos. De los ejemplos presentados aquí, algunos reducen la brecha conceptual aumentando el nivel del lenguaje de ensamblador. Otros ejemplos estudiados la eliminan por completo quitando el ensamblador y haciendo uso de la m5auina

1

Page 57: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

LAN.

En las arquitecturas tradicionales, el pro- grama de LAN pasa una extensa traducción (compilación) para llegar a ejecutarse en un relativamente bajo lenguaje maquina. En el otro extremo, la arquitectura de ejecución directa, el LAN es el lenguaje mhquina. Es decir, el programa en LAN es ejecutado direc- tamente sin compilación intermedia. Y tam- bien existen arquitecturas intermedias que son una combinación de ambas. Ejemplo de ello es el lenguaje maquina del 68020. Ya que existen instrucciones para manejo de matematica de punto flotante directas en ensamblador, las cuales son accesibles desde lenguajes de alto nivel como BASIC, C, Pascal o Modula.

59 Existe otro tipo de arquitecturas que se

denominan de tipo reducido. Estas reducen a la brecha conceptual mejorando el nivel de compilación utilizando solo las instrucciones mas utilizadas para implantarlas en el Hard- ware. De esta manera en el paso de compi- lación se logra el mejoramiento.

IBM 801. Este es un sistema experi- mental empezado en 1975. Lo que se deseaba era mejorar la relación precio/performance para ejecutar programas en LAN.

Una de las mayores características en la 80 1 es que las instrucciones se ejecutan en un solo ciclo de mAquina. Por lo que son relati- vamente simples las instrucciones compara- das con las de arquitecturas mas complejas. Otra de las características del IBM 801 es el reducir el tiempo perdido del CPU debido al acceso a memoria secundaria.

Page 58: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

60

BIBLIOGRAFIA

[Ahuja 861 S. Carrier0 Ahuja and Gelernter D. “Linda and friends”, Computer, vol 19, num 8, 1986

[Bal et al 891 Henry Bal, Jennifer G Steiner and Andrew S. Tanenbaum “Programming Languajes for Distributed Computer Systems” ACM Computing Surveys, vol 2!1, núm 3, septiembre 1989

[Bandes 871 Ken Bandes “C+ + for MS-DOS” Computer Language Mayo 1987

[Black 871 Andrew Black, Norman Hutchinson et al “Distribution and Abstract Types in Emerald” IEEE Transactions on software engineering. Vol SE-13, no 1, January 198‘7

[Birrell 851 Andrew D. Birrell “Secure Comunication using remote procedure calls” ACM Transactions on Computer Systems Vol 3, num 1, febrero 1985

[Clocksin 811 Clocksin and Mellish “Programming in Prolog” Springer-Verlag 198 1

[Dewhustr-Stark 871 Stephen C. Dewhlustr and Kathy T. Stark “Out of the C world comes C+ +” Computer Language, febrero 1987

[Dijkstra 751 EJ. Dijkstra “Guarded commands, nondeterminancy, and formal derivation of pro- grams”. Commun. ACM, vol 18, núm 8, agosto 1975

[Filman 841 Filman Robert E. and Daniel P. Friedman “COORDINATED COMPUTING. Tools and Techniques for distributed software” Ed. MacGrawn-Hill 1984

Page 59: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

61 [Goldberg 831 Goldberg and Robsorl

“Smalltalk-80: the language ans its implementation” Addison-Wesley, Realding, Mass, 1983

[Hansen 871 Brinch Hansen “Joyce- A proggramming lalnguage for distributed systems” Soft. Pract. Exper, vól 17, nlim 1, enero 1987

[Hoare 781 C.A.R. Hoare “Communicanting Sequential Processes” Commun. ACM, vó12 1, núrn 8, agosto 1978

[INMOS 841 INMOS Limited “Occam programming man-ual” Prentice-Hall International, 1987

Uanello et al - 901 G. Janello, A Mazzeo,, C Savy and G. Ventre “Parallel Software Devenlopment in the DISC programming environ- ment”

[Magge et al - 891 Jeff Magee, Jeff Kranler and Morris Sloman “Constructing Distributed Systems in Conic” IEEE Transactions on Software Engineering Vol 15, no 6, Junio 1!389

[Miller 881 William M. Miller “Error Handling in C+ +” Computer Language Mayo 1989

1 4 7 6 8 6

[Oktaba 851 Hanna Oktaba “Programación concurrente (primera parte)” IIMAS Comunicaciones TCcnicas, serie azul, monografia #86

[Oktaba 871 Hanna Oktaba 4 4 Programación concurrente (primera parte)” IIMAS Comunicaciones TCcnicas, serie azul, monografia # 1 O0

[Perez C. 861 Pérez Castañeda Juan Carlos “Redes de Petri” IIMAS Comunicacio~nes TCcnicas, serie azul, monografia #97

[Sethi 891 Ravi Sethi “Programming Languages” Addison Wesley, 1 a edición, 1989

Page 60: PROYECTO 'TERMINAL I I1 LICENCIATURA EN …148.206.53.84/tesiuami/UAM8733.pdf · 8 CAPITULO I1 HISTORIA DE LA PROGRAMACION CONCURRENTE En la historia de la computación los avances

62 [Strom-Yemini 861 Rob Strom and Shaula E’emini

“The NIL Distributed Systems programming Languages: A Status Report” SIGPLAN Notices , vol 5!0, no 5, mayo 1985

[Tanenbaum 891 Andrew S. Tanenbaum “Distributed Operating Systems” Computer Surveys, Vol 17, núm 4, diciembre 1985

[Terrence 871 Terrence W. Pratt Lenguajes de programación Prentice-Hall, 2a edición, 1987

[Tucker 881 Allen B. Tucker Lenguajes de programación Mc Grawn Hill, l a edición, 1988

[Wilkes 861

[Wirth 781

Wilkes and Le Blanc “Rationale for the design of Aeolus: A systems programming language for an action/objet system” Proceedings of the IEEE CS 1986 International Conference on Computer Languages (Miami Florida, Oct) IEEE, New York, pp 107-122

Wirth, N.; Jensen K. “Pascal User manual and report” Springer-Verlag , 2a edición, 1978