CAPíTULO 2 Parallel Virtual Machine (PVM)

Transcripción

CAPíTULO 2 Parallel Virtual Machine (PVM)
PROLOG0
El objetivo por el cual decidimos desarrollar éste proyecto, fue la necesidad
de implementar las soluciones que se dan para resolver los problemas de la
programación concurrente, ya que estas soluciones se quedan en un nivel de
abstracción que no nos permite captar con claridad sus alcances. Es decir cuando
llevamos un curso completo de sistemas operativos nos enseñan cuales son los
problemas típicos y como resolverlos usando distintos algoritmos, sin embargo
nunca podemos programarlos y esto debido a que no contamos con los recursos
necesarios ( computadoras de procesamiento paralelo ).
No obstante existe un software de dominio publico llamado PVM (Parallel
Virtual Machine, Máquina Virtual Paralela) que permite a un conjunto de
computadoras conectadas en red, simular una computadora de procesamiento
paralelo. Esto parecería una forma de obtener un poder de computo mucho más
barato, ya que con un conjunto de computadoras de un costo mucho menor
(80486 con LINUX) que el de una computadora paralela se pueden realizar los
mismos procesos, sin embargo existen muchos factores que se tendrían que
evaluar para comparar esta ventaja aparentemente importante.
Para nuestros fines lo importante era lograr la sincronización entre
procesos, que cooperaran en la solución de un problema aprovechando las
ventajas de las distintas arquitecturas de computadoras que conforman una red.
El material de este reporte se organiza en cuatro capítulos. La cobertura de
estos es secuencia1 partiendo de la noción básica de procesos, la programación
concurrente y los problemas que en ella se generan, con esto se busca dar las
bases para comprender las eventualidades al programar en forma concurrente, en
los demás capítulos se describe que es PVM, sus características y lo más
importante, como utilizarlo para solucionar los problemas de la comunicación entre
procesos.
En el capítulo 1 se proporciona una visión de lo que es un proceso (los
estados por los que pasa y las operaciones que se realizan sobre ellos), los
problemas de sincronización relativos a la ejecución concurrente, las soluciones
que se han dado a estos problemas, también se trata el abrazo mortal que debido
a su importancia en la programación concurrente se describe a detalle y concluye
con la presentación de algunos problemas clásicos de programación.
En el capitulo 2 se describe que es PVM, como se obtiene, la forma en que
se instala, el procedimiento para configurarlo y cuales son los modos en los que
es posible operarlo, se complementa con algunas recomendaciones para resolver
problemas y un breve comentario acerca de las limitantes de su operación.
En el tercer capitulo se dan los detalles de implementación con PVM. Se
explican las características de la programación que emplea, se describen las
herramientas de las que se sirve para funcionar adecuadamente, las bibliotecas
que utiliza, la forma en que realiza la comunicación y concluye con algunas
consideraciones sobre el desempeño y la red.
Para concluir se ofrece en el ultimo capitulo algunos ejemplos de
programación utilizando PVM, consideraciones para escribir aplicaciones, los
elementos que el programador puede utilizar, la forma en que puede realizar la
compilación y ejecución de sus programas y el método de depuración utilizado en
PVM.
Como se puede observar en este reporte se hace mucho énfasis en el
manejo de PVM y la razón es que si se logra comprender la forma en que
funciona y las ventajas que ofrece para los programas concurrentes resultara más
sencillo programar los algoritmos que resuelven los problemas de comunicación
entre procesos.
González Hernández Victor Adrián
López Rivera Yolanda
Zarate Cano José Manuel.
Verano 1998.
AGRADECIMIENTOS :
A M I S PADRES
:
P o r d a r m e l a o p o r t u n i d a d d e ser u n a
p r o f e s i o n i s t a y p o r t o d o s u a p o y o y c o m p r e n s i ó n en
m i s momentos difíciles.
A M I S HERMANOS
:
Por e s t a r conmigo y s o p o r t a r m i c a r á c t e r . Por
d a r v i d a a m i h o g a r y p o r ser m i s h e r m a n o s .
A MARCO ANTONIO
VELÁZQUEZ :
P o r todo s u a p o y o m o r a l y s e n t i m e n t a l d u r a n t e el
t r a n s c u r s o d e m i c a r r e r a T.Q.M.
A V I C T O R GONZÁLEZ Y MANUEL ZARATE
:
P o r t o d o s los m o m e n t o s q u e c o m p a r t i m o s j u n t o s y
p o r d a r m e l a g r a t i t u d d e ser m i s m e j o r e s a m i g o s .
G r a c i a s por soportarme.
A MANUEL A G U I L A R CORNEJO :
P o r q u e m á s q u e ser n u e s t r o a s e s o r , e s u n g r a n
amigo.
YOLANDA LOPEZ R I V E R A .
A g r a d e z c o a Dios p o r p e r m i t i r m e l l e g a r a e s t e
momen t o .
A m i s p a d r e s A n a M a r í a y Amado p o r t o d o s u c a r i ñ o ,
comprensión y a p o y o en t o d o m o m e n t o a l o l a r g o d e m i
carrera.
A m i s h e r m a n o s Ana M a r í a , A n g e l i c a , A l m a , L u i s y
Ricardo por soportarme y ayudarme.
A M a r í a E l e n a por l l e n a r m i c o r a z ó n y a l e n t a r m e a
s e g u i r a d e l a n t e en m i c a r r e r a .
A m i s a m i g o s Y o l a n d a LÓpez y M a n u e l
Zarate por
c o m p a r t i r conmigo s u a m i s t a d y todos estos a ñ o s d e
esfuerzo conjunto.
A M a n u e l A g u i l a r C o r n e j o p o r ser m i g u í a y m o d e l o a
s e g u i r como p r o f e s i o n i s t a , a d e m á s d e ser u n g r a n
amigo y un g r a n a p o y o p a r a nosotros tres. " G r a c i a s
M a n u e l p o r ser m i a m i g o " .
A t o d o s m i s p r o f e s o r e s , q u e g r a c i a s a e l l o s hoy
puedo d i s f r u t a r de este momento.
G O N Z A L E Z HERNANDEZ V I C T O R A D R I A N .
A m i mama
que m e apoyo h a s t a el f i n a l . .
JOSE MANUEL ZARATE CANO
..
Int roducción
El procesamiento en paralelo, es el método que consiste en tener varias
tareas que resuelven un problema. Desde hace varios años se ha incrementado,
la aceptación y la adopción del procesamiento en paralelo, debido ha la aparición
de aplicaciones de “propósito general más complejas”, lo que ha provocado una
demanda por mayor capacidad de ejecución, bajos costos y una productividad
sosten ida.
Los procesadores masivamente paralelos (MPPs) son ahora parte de las
computadoras más poderosas en el mundo. Estas máquinas combinan desde
algunos cientos hasta varios miles de CPUs en un gabinete conectado a cientos
de Gigabytes de memoria. Los MPPs ofrecen un enorme poder computacional y
son usados para resolver problemas computacionales.
El segundo mayor desarrollo que afecta la solución de problemas científicos
es el computo distribuido. El computo distribuido es un proceso donde un conjunto
de computadoras conectadas en red, son usadas colectivamente para resolver
problemas. La combinación de recursos computacionales puede exceder el poder
de una sola computadora con gran poder de computo. En algunos casos, varios
MPPs han sido combinados utilizando computo distribuido produciendo un poder
computacional sin comparación.
El factor más importante en el computo distribuido es el costo. Los grandes
MPPs normalmente cuestan millones de dólares . En contraste los usuarios tienen
un costo mucho menor en la ejecución de sus problemas con un conjunto de
maquinas locales. Esto no es común para los usuarios de computo distribuido que
utilizan las enormes MPP con su gran poder de computo ya que ellos aseguran
que con estas máquinas que obtendrán los resultados en un tiempo mucho menor
que el usuario que esta usando sus computadoras locales.
Varios paradigmas han sido probados incluyendo memoria compartida,
compiladores paralelos, y paso de mensajes. El modelo de paso de mensajes ha
llegado a ser el paradigma de opción, desde la perspectiva del número y variedad
de multiprocesadores que soporta, así como en términos de las aplicaciones,
lenguajes, y sistemas de software que este usa.
El sistema Parallel Virtual Machine (PVM) describe el uso del modelo de
paso de mensajes para permitir a los programadores explotar el computo
distribuido de acuerdo con los de tipos de computadoras, incluidas las MPPs que
soporta. Un concepto clave en PVM es que éste crea una colección de
computadoras que simula una gran máquina virtual.
Conten ido
Prólogo
Agradecimientos
Introducción ---,
Capitulo 1.
.-I
Procesos
1.INociones Básicas De Proceso
1.I.
1 Estados De Un Proceso
1.I
.2 El Bloque De Control De Proceso
.3 Operaciones Sobre Los Procesos
1.I
1.2 Procesos En Sistemas Distribuidos
1.2.1 Introducción A Los Sistemas Distribuidos
1.2.2 Comunicación En Sistemas Distribuidos
1.2.3 Sincronización En Sistemas Distribuidos
1.2.3.1 Exclusión Mutua
1.2.4 Paso De Mensajes
1.3 Abrazos Mortales
1.3.1 Definición
1.3.2 Modelación De Un Abrazo Mortal
1.3.3 Características Del Abrazo Mortal
1.3.4 Prevención Del Abrazo Mortal
1.3.5 Evitando Abrazos Mortales
1.3.6 Recuperación Del Abrazo Mortal
1.4 Problemas De La Programación Concurrente
_''
Capitulo 2.
Parallel Virtual Machine (PVM).
2.1 Introducción
2.2 Obteniendo E Instalando Pvm
2.3 Configuración De La Maquina Virtual
2.3.1 La Consola Pvm
2.3.2 El Archivo Hostfile
2.3.3 Variables De Ambiente
2.3.4 Problemas AI Iniciar Pvm
2.4 Limitaciones De Recursos
2.4.1 En El Demonio Pvm
2.4.2 En La Tarea
2.5 Detalles De Implantación
2.5.1 Introducción
2.5.2 Características De Pvm
2.5.2.1 ldentificador De Tareas
2.5.2.2 Tolerancia A Fallas
2 5 2 . 3 Señalización
2.5.2.4 Comunicación
1
1
2
2
3
3
4
5
6
10
12
12
13
13
13
14
15
16
21
22
24
24
26
29
30
31
31
31
32
32
32
32
34
35
35
2.6 El Demonio Pvm
2.6.1 Inicio De Pvmd
2.6.2 Tabla De Hosts
2.6.3 Tabla De Tareas
2.6.4 Contexto De Espera
2.6.5 Detección Y Recuperación De Fallas
2.7 La Biblioteca De Programación
2.8 Comunicación
2.8.1 Comunicación Pvmd-Pvmd
2.8.2 Comunicación Tarea - Pvmd
2.8.3 Comunicación Pvmd - Tarea
2.8.4 Mensajes En El Pvmd
2.8.5 Codificadores De Mensajes
2.8.6 Funciones Que Manejan Empaquetamiento
2.8.7 Mensajes De Control
2.8.8 Ruteo Directo De Mensajes
2.8.9 Envío Múltiple
2.9 Consideraciones Generales De Desempeño
2.1O Consideraciones Particulares De La Red
2.1 1 Balance De Carga
Capitulo 3.
Aplicaciones bajo PVM.
3.1 Ejemplos De Programación De Pvm En Lenguaje C
3.2 Escribiendo Aplicaciones
3.3 lnterfaz De Usuario
3.3.1 Control De Procesos
3.3.2 Información
3.3.3 Configuración Dinámica
3.3.4 Señalización
3.3.5 Opciones De Colocación Y Obtención
3.4 Paso De Mensajes
3.4.1 Buffers De Mensajes
3.4.2 Empaquetamiento De Datos
3.4.3 Envío Y Recepción De Datos
3.4.4 Desempaquetamiento De Datos
3.5 Compilando Aplicaciones Pvm
3.6 Métodos De Depuración
3.7 Ejecutando Aplicaciones Pvm
Capitulo 4
Solución al problema de la cena de los filósofos
4.1 Planteamiento
4.2 Implantación En Un Sistema Distribuido Usando Pvm
Bibliografía
35
36
38
39
39
40
40
41
41
44
44
45
45
46
46
46
47
47
48
49
51
56
57
57
58
59
60
60
61
62
64
64
66
67
67
70
71
73
86
CAPíTULO I
Procesos
1.1 Nociones básicas de proceso
El término proceso fue utilizado por primera vez por los diseñadores del sistema
Mulfics’ en los años sesenta. Desde entonces, el término proceso, utilizado a veces como
sinónimo de tarea, ha tenido muchas definiciones ( H.M. Deitel,l993).
A continuación se presentan algunas:
0
0
0
0
0
0
0
un programa en ejecución.
una actividad asíncrona.
el espíritu animado de un procedimiento
el “centro de control de un procedimiento en ejecución.
lo que se manifiesta por la existencia de un bloque de control del proceso ” en el
sistema operativo.
la entidad a la que se asignan los procesadores.
la unidad “despachable
‘I
‘I
‘I
‘I
‘I.
Aunque se han dado muchas otras definiciones, no hay una descripción
universalmente aceptada, pero el concepto de “programa en ejecución“ parece ser el que se
utiliza con mas frecuencia. Un programa es una entidad inanimada; sólo cuando un
procesador le “infunde vidaJJ
se convierte en la entidad “activa” que se denomina proceso.
1.I .I Estados de un proceso.
Un proceso pasa por una serie de estados discretos. Varios eventos pueden
ocasionar que un proceso cambie de estado.
Se dice que un proceso se está ejecutando (es decir, se encuentra en estado de
ejecución) si tiene asignada la CPU. Se dice que un proceso está listo (es decir, se
encuentra en estado listo) si pudiera utilizar una CPU en caso de haber una disponible. Un
proceso esta bloqueado (es decir, se encuentra en estado bloqueado) si está esperando que
suceda algún evento (como la terminación de una €/S, por ejemplo) antes de poder
proseguir su ejecución.
’
Sistema operativo de tiempo compartido, escrito principalmenteen un lenguaje de alto nivel ,desarrolladopor un grupo de instituciones en
MIT ( Massachusetts Institute of Technology ).
2 Procesos
Cuando se admite una tarea en el sistema, se crea el proceso correspondiente y se
inserta normalmente al final de la cola de procesos listos. El proceso se desplaza poco a
poco hacia el frente de la cola de procesos, a medida que los procesos que se encuentran
antes que él completan su turno de uso de la CPU.
Cuando el proceso llega al principio de la cola, se le asigna tiempo de CPU y entonces
se dice que hay una transición de estado, del estado listo al estado de ejecución.
1.1.2 El Bloque de Control de Proceso
La forma en que se manifiesta un proceso en un sistema operativo, es mediante un
bloque de control de proceso (process control block, PCB) ó un descriptor de proceso. El
PCB es una estructura de datos que contiene información importante acerca de un proceso,
tal como:
0
0
0
0
0
0
0
0
0
el estado actual del proceso
un identificador único del proceso
un apuntador hacia el padre del proceso (es decir, hacia el proceso que lo creó)
apuntadores a los hijos del proceso (es decir, a los procesos creados por él)
la prioridad del proceso
apuntadores hacia las zonas de memoria del proceso
apuntadores a los recursos asignados al proceso
un área para salvaguarda de los registros
el procesador en que se está ejecutando el proceso (en un sistema de
procesadores mÚIt¡ples)
El PCB es un almacén central de información que permite al sistema operativo
localizar toda la información importante acerca de un proceso.
I.I .3 Operaciones sobre los Procesos
Los sistemas que administran procesos deben ser capaces de realizar ciertas
operaciones sobre procesos y con ellos. Tales operaciones incluyen:
crear un proceso
destruir un proceso
suspender un proceso
reanudar un proceso
cambiar la prioridad de un proceso
bloquear un proceso
despertar un proceso
despachar un proceso
permitir que un proceso se comunique con otro (esto se denomina comunicación
entre procesos)
Procesos en Sistemas Distribuidos 3
I .2
Crear un proceso implica muchas operaciones tales como:
0
0
0
dar nombre al proceso
insertarlo en la lista de procesos conocidos del sistema ( o tabla de procesos)
determinar la prioridad inicial del proceso
crear el bloque de control de proceso
asignar los recursos iniciales al proceso, etc.
Un proceso puede crear un nuevo proceso. Si lo hace, el proceso creador se
denomina proceso padre, y el proceso creado, proceso hijo. Sólo se necesita un padre para
crear un hijo.
Destruir un proceso implica eliminarlo del sistema. Se le elimina de las tablas o listas
del sistema, sus recursos se devuelven al sistema y su bloque de control de proceso se borra
(es decir, el espacio de memoria ocupado por su PCB se devuelve al espacio de memoria
disponible).
Un proceso suspendido no puede proseguir sino hasta que lo reanuda otro proceso, el
sistema efectúa las suspensiones para eliminar temporalmente ciertos procesos y así reducir
la carga del sistema durante una situación de carga máxima. Reanudar o activar un proceso
implica reiniciarlo a partir del punto en el que se suspendió.
1.2 Procesos en Sistemas Distribuidos
I.2A Introducción a los Sistemas Distribuidos
En el pasado, la mayoría de las computadoras trabajaba en forma aislada y la mayoría
de los Sistemas Operativos eran diseñados para su ejecución en un solo procesador. Sin
embargo, esta situación ha evolucionado gracias a dos avances tecnológicos. El primero fue
el desarrollo de poderosos microprocesadores y el segundo fue la invención de redes de
área local de alta velocidad (Local Area Network, LAN).
Las redes de área local permitieron conectar docenas e incluso cientos de máquinas
de tal forma que se pudiera transferir pequeñas cantidades de información entre ellas
durante un milisegundo o un tiempo parecido(Tanembaum, 1996).
El resultado de estos dos avances tecnológicos es que hoy en día es posible reunir
sistemas de cómputo compuestos por un gran número de CPU’s, conectados mediante una
red de alta velocidad. Éstos reciben el nombre genérico de Sistemas Distribuidos, en
contraste con los Sistemas Centralizados,que constan de un único CPU.
4 Procesos
“Un Sistema Distribuido es aquel que se ejecuta en una colección de máquinas sin
memoria compartida, pero que aparece ante sus usuarios como una sola computadora”
(Tanenbaum, 1996)
Algunas de las características de un Sistema Distribuido son:
Un mecanismo de comunicación global entre los procesos, de tal forma que todo
proceso pueda comunicarse con cualquier otro.
No es necesario que existan distintos mecanismos en cada máquina para la
comunicación local ó remota.
Debe existir un esquema global de protección.
La administración de procesos debe ser la misma en todas partes.
Los Sistemas Distribuidos deben diseñarse con cuidado, teniendo en cuenta los
siguientes aspectos :
La Transparencia : Ocultar la distribución a los usuarios e incluso de los programas
de aplicación.
La Flexibilidad : El diseño se debe hacer con la idea de facilitar los cambios futuros.
La Confiabilidad : Disponibilidad de la información, la seguridad de la misma y
tolerancia a fallos del sistema.
El Desempeño : El tiempo de respuesta y el rendimiento.
La Escalabilidad : Proporciona soporte al crecimiento del sistema.
I.2.2 Comunicación en Sistemas Distribuidos
La diferencia más importante entre un sistema distribuido y un sistema de un Único
procesador es la comunicación entre procesos. En un sistema con un solo procesador, la
mayor parte de la comunicación entre procesos supone de manera implícita la existencia de
memoria compartida. En un sistema distribuido, no existe tal memoria compartida.
Debido a la ausencia de memoria compartida, toda la comunicación en los Sistemas
Distribuidos se basa en la transferencia de mensajes. Cuando el proceso A quiere
comunicarse con el proceso 9, construye primero un mensaje en su propio espacio de
direcciones, entonces ejecuta una llamada al sistema para que el Sistema Operativo busque
el mensaje y lo envíe a través de la red hacia B. Para evitar el caos, A y B deben coincidir en
el significado de los bits que se envíen.
Para facilitar el trabajo con los distintos aspectos correspondientes a la comunicación,
la Organización Internacional de Estándares (International Standard Organization, ISO), ha
desarrollado un modelo de referencia que identifica en forma clara los distintos niveles de
comunicación, les da nombres estandarizados y señala cuál nivel debe realízar cuál trabajo.
Este modelo se llama el modelo de referencia para interconexión de sistemas abierfos (Day y
Zimmerman, 1983), lo cual se abrevia por lo general con IS0 OSl(0pen System
Interconection). Un sistema abierto es aquel preparado para comunicarse con cualquier
sistema abierto mediante reglas estándar que gobiernan el formato, contenido y significado
de los mensajes enviados y recibidos (Tanembaum, 1992). Estas reglas se formalizan en lo
que se llama protocolos.
I .2
Procesos en Sistemas Distribuidos 5
El Protocolo de Control de Transmisión (Transmition Control Protocol, TCP) y el
Protocolo Internet (Internet Protocol, IP) proporcionan las reglas para la comunicación.
Contienen los detalles referentes a los formatos de los mensajes, describen como responde
una computadora cuando llega un mensaje y especifican de qué manera una computadora
maneja un error u otras condiciones anormales. Un aspecto importante es que permite
reflexionar sobre la comunicación por computadora de manera independiente de cualquier
plataforma de red. Un protocolo de comunicaciones permite especificar o entender la
comunicación de datos sin depender de un conocimiento detallado de un hardware de red.
El TCP define un servicio clave proporcionado para una red de redes llamado entrega
de flujo confiable. El TCP proporciona una conexión del tipo full duplex (en ambas
direcciones), lo que les permite intercambiar grandes volúmenes de datos de manera eficaz.
Debido a que se hacen pocas suposiciones sobre el sistema de entrega subyacente,
el TCP es lo suficientemente flexible como para operar sobre una gran variedad de sistemas
de entrega. Ya que proporciona un control de flujo, el TCP permite que el sistema cuente con
una amplia variedad de velocidades para la comunicación.
El Protocolo de Datagrama de Usuario (User Datagram Protocol, UDP) proporciona un
servicio de entrega sin conexión y no confiable, utilizando el IP para transportar mensajes
entre máquinas y agrega la capacidad para distinguir entre varios destinos dentro de un
mismo host.
El UDP es un protocolo sencillo en el sentido de que no aumenta de manera
significativa la semántica del IP. Sólo proporciona a los programas de aplicación la capacidad
para comunicarse, mediante el uso del servicio de entrega de paquetes, sin conexión y no
confiable. Por lo tanto, los mensajes UDP se pueden perder, duplicar, retrasar o entregar en
desorden; el programa de aplicación que utiliza el UDP debe resolver éstos problemas.
Muchos programas que emplean el UDP no funcionan correctamente en una red de redes
debido a que no manejan éstas condiciones. (Comer, 1995).
I .2.3 Sincronización en Sistemas Distribuidos
Aunque la comunicación es importante, no es todo lo que hay que considerar. Algo
muy relacionado con esto es la forma en que los procesos cooperan y se sincronizan entre
sí. Por ejemplo, la forma de implantar las regiones críticas o asignar recursos en un Sistema
Distribuido.
En los sistemas que sólo cuentan con un CPU, los problemas relativos a las regiones
críticas, exclusión mutua y la sincronización se resuelven en general mediante métodos tales
como los semáforos y los monitores. Estos métodos no son adecuados para su uso en los
Sistemas Distribuidos, puesto que siempre se basan en la existencia de la memoria
compartida. Por ejemplo, dos procesos que iteractuan mediante un semáforo deben poder
tener acceso a éste. Si se ejecutan en la misma máquina, pueden compartir el semáforo
almacenado en el núcleo y realizar llamadas al sistema para tener acceso a él. Sin embargo,
si se ejecutan en máquinas distintas, este método ya no funciona, por lo que se necesitan
otras técnicas. Incluso las cuestiones más sencillas, como el hecho de determinar si el
evento A ocurrió antes o después del evento B requieren una cuidadosa reflexión.
6 Procesos
1.2.3.1 Exclusión Mutua
Los procesos que trabajan juntos comparten con frecuencia un espacio común para
almacenamiento, en el que cada uno puede leer o escribir. El espacio compartido puede
estar en la memoria principal o ser un archivo compartido. En el caso de los Sistemas
Distribuidos no existe tal memoria compartida, por lo que los problemas se reducen a
compartir otros recursos (impresoras, buffers de comunicación, archivos, etc.).
Las situaciones en las que dos o más procesos leen o escriben en ciertos datos
compartidos y el resultado final depende de quién ejecuta qué y en qué momento, reciben el
nombre de condiciones de competencia.
La clave para evitar las condiciones de competencia relacionadas con la memoria
compartida, archivos compartidos y cualquier otra cosa compartida, es determinar una forma
de prohibir que más de un proceso lea o escriba en los datos compartidos a la vez. En otras
palabras lo que necesitamos es la exclusión mutua (una forma de garantizar que si un
proceso utiliza una variable o archivo compartidos, los demás procesos no puedan
utilizarlos).
Sin embargo, en algunas ocasiones un proceso puede tener acceso a los recursos
compartidos o realizar labores críticas que puedan llevar a conflictos. Esa parte del
programa, en la que se tiene acceso a los recursos compartidos se llama la región crítica. Si
podemos arreglar las cosas de forma que no ocurra que dos procesos están al mismo tiempo
en su región crítica, podremos evitar las condiciones de competencia.
Aunque esta condición evita los conflictos, no es suficiente para que los procesos
paralelos cooperen en forma correcta y usen de modo eficaz los datos compartidos.
Necesitamos cuatro condiciones para poder obtener una buena solución:
1. Dos procesos no deben encontrarse al mismo tiempo dentro de sus secciones
críticas.
2. No se deben hacer hipótesis sobre la velocidad o el número de CPU.
3. Ninguno de los procesos que estén en ejecución fuera de su sección crítica puede
bloquear a otros procesos.
4. Ningún proceso debe esperar eternamente para entrar a su sección crítica.
Los sistemas con varios procesos se programan comunmente mediante las regiones
críticas. Cuando un proceso debe leer o actualizar ciertas estructuras de datos compartidas,
primero entra a una región crítica para lograr la exclusión mutua y garantizar que ningún otro
proceso utilice las estructuras de datos al mismo tiempo.
Analizaremos ahora algunos ejemplos de implantación de las regiones críticas y la
exclusión mutua en los Sistemas Distribuidos.
1.2
Procesos en Sistemas Distribuidos 7
Algoritmo Centralizado. La forma más directa de lograr la exclusión mutua en un
Sistema Distribuido es similar a la forma en que se lleva a cabo en un sistema con un
procesador. Se elige un proceso como coordinador. Siempre que un proceso desea entrar a
una región crítica, envía un mensaje de solicitud al coordinador, donde indica la región crítica
a la que desea entrar y pide permiso. Si ningún otro proceso está por el momento en esa
región critica, el coordinador envía una respuesta otorgando el permiso. Cuando llega la
respuesta, el proceso solicitante entra a la región crítica. Supongamos ahora que otro
proceso, pide permiso para entrar a la misma región crítica. El coordinador sabe que un
proceso distinto ya se encuentra en esta región, por lo que no puede otorgar el permiso. El
método exacto utilizado para negar el permiso depende del sistema. El coordinador sólo se
abstiene de responder, con lo cuál se bloquea el proceso que espera una respuesta. Otra
alternativa consiste en enviar una respuesta que diga “permiso denegado”.
Cuando el proceso sale de la región crítica, envía un mensaje al coordinador para
liberar su acceso exclusivo. El coordinador extrae el primer elemento de la fila de solicitudes
diferidas y envía a ese proceso un mensaje otorgando el permiso. Si el proceso está
bloqueado, elimina el bloqueo y entra a la región crítica. Si ya se envió un mensaje explícito
negando el permiso, entonces el proceso debe hacer un muestre0 del tráfico recibido o
bloquearse posteriormente. De cualquier forma, cuando ve el permiso, puede entrar a la
región crítica.
El método centralizado tiene limitaciones. El coordinador es un punto de falla, por lo
que si se descompone, todo el sistema se puede venir abajo. Si los procesos se bloquean
por lo general después de realizar una solicitud, no pueden distinguir entre un coordinador
muerto de un ”permiso denegado”, puesto en ambos casos no reciben respuesta. Además en
un sistema de gran tamaño, un coordinador puede convertirse en un cuello de botella
(congestionamiento de peticiones) para el desempeño.
Algoritmo Distribuido. Con frecuencia, el hecho de tener un punto de falla es
inaceptable, por lo cual algunos investigadores (Lamport, 1978)(Ricart y Agrawala, 1981)
buscaron algoritmos distribuidos de exclusión mutua.
El algoritmo resultante funciona como sigue: cuando un proceso desea entrar a la
región crítica, construye un mensaje con el nombre de ésta, su número de proceso y la hora
actual. Entonces envía el mensaje a todos los demás procesos y de manera conceptual a él
mismo. Se supone que el envío de mensajes es confiable; es decir, cada mensaje tiene un
reconocimiento. Si se dispone de una comunicación en grupo confiable, entonces ésta se
puede utilizar en vez del envío de mensajes individuales.
Cuando un proceso recibe un mensaje de solicitud de otro proceso, la acción que
realice depende de su estado con respecto de la región crítica nombrada en el mensaje.
8 Procesos
Hay que distinguir tres casos:
1. Si el receptor no está en la región crítica y no desea entrar en ella, envía de regreso
un mensaje OK al emisor.
2. Si el receptor ya está en la región crítica, no responde, sino forma la solicitud en
una fila.
3. Si el receptor desea entrar a la región crítica, pero no lo ha logrado todavía,
compara la marca de tiempo en el mensaje recibido con la marca contenida en el
mensaje que envió a cada uno. La menor de las marcas gana. Si el mensaje
recibido es menor, el receptor envía de regreso un mensaje OK. Si su propio
mensaje tiene una marca menor, el receptor forma la solicitud en una fila y no envía
nada.
Después de enviar las solicitudes que piden permiso para entrar a una región crítica,
un procesador espera hasta que alguien más obtiene el permiso. Tan pronto llegan todos los
permisos, puede entrar a la región crítica. Cuando sale de ella, envía mensajes OK a todos
los procesos en su fila y elimina a todos los elementos de la fila.
Como en el caso del algoritmo centralizado, la exclusión mutua queda garantizada sin
bloqueo ni inanición. El número de mensajes necesarios por entrada es ahora de 2(n-1), en
donde n es el número total de procesos en el sistema. Lo mejor es que no existe un punto de
falla.
Por desgracia, el único punto de falla es reemplazado por n puntos de falla. Si
cualquier proceso falla, no podrá responder a las solicitudes. Este silencio será interpretado
(incorrectamente) como negación del permiso, con lo que se bloquearán los intentos de los
demás procesos por entrar a todas las regiones críticas. Puesto que la probabilidad de que
uno de los n procesos falle es n veces mayor que la probabilidad de que falle un coordinador,
se ha buscado reemplazar un algoritmo pobre con uno que es n veces peor y que requiere
mayor tráfico en la red para funcionar.
El algoritmo se puede mejorar de la siguiente manera: AI llegar una solicitud, el
receptor siempre envía una respuesta, otorgando o negando el permiso. Siempre que pierda
una solicitud o una respuesta, el emisor espera y sigue intentando hasta que le regresan una
respuesta o el emisor concluye que el destino esta muerto. Después de negar una solicitud,
el emisor debe bloquearse en espera de un mensaje de OK posterior.
Otro problema con este algoritmo es que se debe utilizar una primitiva de
comunicación en grupo; o bien, cada proceso debe mantener por sí mismo la lista de
membresía del grupo, donde se incluyan los procesos que ingresan al grupo, los que salen
de él y los que fallan. El método funciona mejor con grupos pequeños de procesos que
nunca cambian sus membresías de grupo.
En el algoritmo distribuido, todos los procesos participan en todas las decisiones
referentes a la entrada en las regiones críticas. Si un proceso no puede manejar esta tarea,
es poco probable que si todos los procesos la realizan sirva de algo. Sin embargo este
algoritmo es más lento, más complejo, más caro y menos robusto que el algoritmo
centralizado original, pero se mostró la posibilidad de un algoritmo distribuido, situación que
no era obvia en un principio.
1.2
Procesos en Sistemas Distribuidos 9
Algoritmo de anillo de fichas. Un método completamente distinto para lograr la
exclusión mutua en un sistema distribuido es el que se logra con el algoritmo de anillo de
fichas el cual consiste de una red basada en un bus, en ésta red los procesos no tienen un
orden inherente. En software, se construye un anillo lógico y a cada proceso se le asigna una
posición en el anillo. Las posiciones en el anillo se pueden asignar en el orden numérico de
las direcciones de la red o mediante algún otro medio. No importa como sea el orden, lo
importante es que cada proceso sepa quien es el siguiente en la fila despues de él.
AI inicializar el anillo, se le da al proceso O una ficha, la cual circula en todo el anillo.
Se transfiere del proceso K al proceso K+1 (módulo el tamaño del anillo) en mensajes
puntuales. Cuando un proceso obtiene la ficha de su vecino, verifica si intenta entrar a una
región crítica. En ese caso, el proceso entra a la región, hace todo el trabajo necesario y sale
de la región. Después de salir, pasa la ficha a lo largo del anillo. No se permite entrar a una
segunda región crítica con la misma ficha.
Si un proceso recibe la ficha de su vecino y no está interesado en entrar a una región
crítica, sólo la vuelve a pasar. En consecuencia, cuando ninguno de los procesos desea
entrar a una región crítica, la ficha sólo circula a gran velocidad en el anillo.
Como es usual también este algoritmo tiene problemas. Si la ficha llega a perderse,
debe ser regenerada. De hecho, es difícil detectar su pérdida, puesto que la cantidad de
tiempo entre las apariciones sucesivas de la ficha en la red no está acotada. El hecho de que
la ficha no se haya observado durante una hora no significa su pérdida; tal vez alguien la
esté utilizando.
El algoritmo también tiene problemas si falla un proceso, pero la recuperación es más
sencilla que en los demás casos. Si pedimos un reconocimiento a cada proceso que reciba la
ficha, entonces se detectará un proceso muerto si su vecino intenta darle la ficha y fracasa
en el intento. En este momento, el proceso muerto se puede eliminar del grupo y el poseedor
de la ficha puede puede enviar ésta por encima de la cabeza del proceso muerto al siguiente
miembro, o al siguiente miembro despues de éste, en caso necesario. Por supuesto esto
requiere que todos mantengan la configuración actual del anillo.
Algoritmos de elección. Muchos de los algoritmos distribuidos necesitan que un
proceso actúe como coordinador, iniciador, secuenciador o que desempeñe de cierta forma
algún papel esencial. En general, no importa cuál de los procesos asuma esta
responsabilidad especial, pero uno de ellos debe hacerlo.
Si todos los procesos son idénticos, sin caracteristica alguna que los distinga, no
existe forma de elegir uno de ellos como especial. En consecuencia, supondremos que cada
proceso tiene un número único; Por ejemplo, su dirección en la red (suponiendo que existe
un proceso por cada máquina). En general, los algoritmos de elección intentan localizar al
proceso con el máximo número de proceso y designarlo como coordinador. Los algoritmos
difieren en la forma en que lo llevan a cabo.
10 Procesos
Además, también supondremos que cada proceso sabe el número de proceso de
todos los demás. Lo que el proceso desconoce es si los procesos están activos o inactivos.
El objetivo de un algoritmo de elección es garantizar que al iniciar una elección, ésta
concluya con el acuerdo de todos los procesos con respecto a la identidad del nuevo
coordinador.
1.2.4 Paso de Mensajes
Las regiones críticas y los monitores son extrapolaciones de los semáforos. 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 requerimiento resulta natural en una
computadora o en un sistema con memoria común. ¿Qué pasa en un sistema distribuido? En
él las computadoras están físicamente 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 semáforos.
En un sistema distribuido tenemos conectadas muchas computadoras independientes
que mandan y reciben mensajes. Estos mensajes 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 semáforos permite un enfoque llamado paso de mensajes
(message passing), el cual puede ser visto como el extender los semáforos para implantar el
mandar datos así como la sincronización. Cuando usamos el paso de mensajes para
sincronización y comunicación, los procesos envían y reciben mensajes en vez de leer y
escribir en variables compartidas.
La comunicación es lograda por que un proceso al recibir un mensaje, recibe también
valores del proceso que se lo envió. La sincronización se obtiene ya que el mensaje es
recibido después 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 irán a dar la lista-expresiones
vista anteriormente. La fuente permite decidir de donde será recibido el mensaje así como
que proceso generó la información solicitada. La recepción del mensaje efectúa la
asignación de valores a las distintas variables y posteriormente destruye el mensaje
permitiendo liberar canales para la recepción de otro mensaje. El diseño de primitivas para
Procesos en Sistemas Distribuidos 11
1.2
paso de mensajes conlleva los problemas de la definición de la semántica de las primitivas
¿Cómo se definen fuente y destino?. Y también el problema de los protocolos de
sincronización. Existen varias soluciones para estos problemas las cuales veremos a
continuación.Tomados juntos Fuente y Destino definen un canal de comunicación.
Necesitamos 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 Naming ). De tal manera que:
Envía ele-prod A Consumidor
envía un mensaje que solo puede ser recibido por el proceso consumidor. De manera
análoga:
Recibe ele-prod De Productor
permite recibir un mensaje enviado por el proceso productor. El nombramiento directo es
fácil de implementar y de usar. Hace posible a un proceso controlar el tiempo en el que
recibe un mensaje de otro proceso.
La solución es muy natural y sencilla de programar. Este modelo permite el
encadenamiento de procesos, llamado también pipeline (tubería). Se tienen a varios
procesos concurrentes en los que los datos de salida 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 más de un proceso que produce cero más de uno que consume?. Tendríamos
que enviar desde cada proceso productor o recibirla desde cada proceso consumidor. Pero
sucede que el nombramiento directo solo nos permite recibir 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 nombramiento directo.
Pensemos en una tienda ahí llega cualquier cliente, algún proceso, el cual es atendido
por un servidor, un proceso como el manejador de la impresora. A este esquema se le
denomina cliente - servidor. Para el cual no sirve el enfoque de nombramiento directo.
Podemos pensar que cada cliente deja una nota donde especifica el trabajo que desea que
se le efectúe. Por otro lado habrá un server que tomará las notas y las atenderá en el orden
en que llegaron. Definimos entonces un buzón (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. Después, 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
sólo un proceso recibirá los mensajes puestos en él. Existe una implantación de puertos
donde al ser enviado un mensaje sólo un proceso puede ser despertado por este hecho,
pero cualquier otro proceso puede acceder al puerto. Se asume que cada proceso verá si
12
Procesos
existe nueva información dentro del puerto y también cada proceso se hace cargo de tomar
sólo los mensajes que le corresponden. La implantación de los puertos no es tan costosa
como la del 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 están los mensajes disponibles
para todos los procesos.
1.3. Abrazos Mortales
I.3.1 Definición
En un ambiente de multiprogramación, muchos procesos compiten por un número
determinado de recursos. Un proceso pide los recursos, y si no todos están disponibles en
ese momento, entonces debe esperar. Puede suceder que el proceso se quede esperando
para siempre ya que los recursos que se necesitan fueron tomados primero por otros
procesos que también están esperando. Si esto se da entre los demás procesos ninguno de
los cuales puede proseguir por estar esperando los recursos que esta esperando otro que
también esta esperando, tenemos un abrazo mortal. Esta sifuación delicada puede ocasionar
la perdida y debe ser evitada en lo posible o manejada adecuadamente una vez que
aparezca.
1.3.2 Modelación de un Abrazo Mortal.
Supongamos que tenemos un sistema el cual consiste de un número finito de
recursos que se distribuyen entre los diferentes procesos que se ejecutan. Los recursos se
pueden dividir en diferentes tipos, cada uno con un cierto número de instancias del mismo.
El CPU, la memoria y los archivos son algunos ejemplos de recursos. Si hay dos CPU en el
sistema entonces el recurso tipo CPU tiene dos instancias. De manera análoga, el recurso
impresora puede tener cinco instancias.
Un proceso debe pedir un recurso antes de utilizarlo y soltarlo una vez usado. Uno
puede pedir tantos recursos como necesite. Es claro que este número no debe exceder el
número disponible en el sistema.
En condiciones normales, el proceso da los siguientes pasos para utilizar un recurso:
1. Petición. Si el recurso no se puede conseguir inmediatamente (si, por ejemplo, el
recurso lo esta utilizando otro proceso), entonces el proceso debe esperar hasta
que le sea otorgado.
2. Uso. El proceso utiliza el recurso.
3. Liberación. El proceso libera el recurso para que este pueda ser utilizado por otros
procesos.
La petición y liberación de recursos varían de acuerdo al tipo de recursos. Algunas se
harán mediante el uso de llamadas al sistema, otras mediante el uso de instrucciones wait y
signal (Francisco Manuel Márquez, 1994). Una tabla en el sistema lleva el registro de la
asignación que se la ha dado a los recursos. Se manejan colas para los distintos tipos de
recursos, de tal suerte que, si un proceso llega a pedir un recurso que esta siendo utilizado
por alguien más se agrega a la cola.
Abrazos Mortales 13
1.3
1.3.3. Características del Abrazo mortal
Para el estudio de los abrazos mortales es necesario estudiar primero sus
características para poder definir estrategias de prevención y manejo. Lo primero es saber
bajo que condiciones se dan. Un abrazo mortal se da si y solo si las siguientes condiciones
se dan en el sistema.
0
0
Exclusión Mutua. AI menos uno de los recursos que esta involucrado no es
compatible; es decir, un y solo un proceso puede usar al recurso a la vez. Si
alguien más lo pide, el proceso que hizo la petición espera hasta que ha sido
Iiberado.
Condición de Espera. Debe existir un proceso que tiene en posesión al menos
un recurso y esta esperando recursos adicionales que están en posesión de otros
procesos.
No Apropiación. Los recursos no pueden ser apropiados; esto es, el recurso solo
puede ser liberado, de manera voluntaria por el proceso que lo esta utilizando, una
vez terminado su uso.
Cola Circular. Debe existir un conjunto de procesos esperando {Po,P, P,} tal que
Poesta esperando un recurso agarrado por Pi, P, esta esperando uno agarrado por
P2 ........ Pn.f esta esperando un recurso agarrado por P, y Poesta esperando un
recurso agarrado por Po.
Estas condiciones son necesarias y suficientes. No se necesita que haya otras
condiciones y faltando alguna de ellas no se dará el abrazo mortal. Utilizar este hecho
permite mucho del análisis de técnicas de prevención y manejo.
1.3.4 Prevención del abrazo mortal.
Como se dijo anteriormente para que el abrazo mortal se dé es necesario que se
cumplan las cuatro condiciones necesarias y suficientes. AI prevenir que al menos una de
ellas se dé estaremos previniendo la aparición del abrazo mortal.
rn Exclusión mutua.- La exclusión mutua no puede eliminarse de recursos. Pero se
W
debe mantener al mínimo, así por ejemplo, las operaciones de lectura pueden
realizarse normalmente sobre cualquier recurso sin necesidad de utilizarlo de
manera única.
Condición de espera.- Se puede utilizar un protocolo que permita al proceso
obtener sus recursos al inicio de su ejecución. Esto permite garantizar que cuando
un proceso está pidiendo sus recurso no tenga ningún otro. Otro protocolo puede
permitir a los procesos pedir recursos sólo cuando no tiene ninguno. Esto se logra
soltando los recursos una vez utilizados y pidiéndolos cada vez que se utilicen.
Este protocolo puede ser demasiado burocrático. Estos protocolos
desgraciadamente no son muy útiles ya que ambas estrategias evitan la correcta
utilización de los recursos. Si un proceso necesita utilizar un recurso sólo una vez,
con el primer protocolo lo mantendra ocupado durante mucho tiempo. Por el otro
lado, si lo necesita muchas veces, el segundo protocolo aumentará la carga de
.-
14
Procesos
pedir y liberar el recurso, existiendo la posibilidad de que el proceso no pueda
ejecutarse si el recurso en cuestión es muy popular.
En general, cada proceso
tendrá recursos que utiliza mucho y otro recurso que casi no. Por ello estos
protocolos tienen valor poco práctico.
No apropiación.- Una forma de prevención es no permitir que un proceso se
apropie de recursos de otros procesos mientras se encuentran en estado de
espera. Esto se utiliza sobre todo en el caso de la memoria, ya que la apropiación
no causa ningún daño al proceso víctima. Sin embargo, no es utilizable en el caso
de recursos donde el proceso se encuentra con operaciones a medio realizar; como
puede ser el caso de las impresoras, las cintas o los archivos en disco.
Cola circular.- Para asegurarse de que no exista nunca la cola circular, se puede
dar una orden en la cual se piden los recursos. Existe una función que para cada
recurso nos da un número natural, que representa el orden del recurso. Los
procesos sólo pueden solicitar recursos cuyo orden es cada vez mayor.
1.3.5 Evitando abrazos mortales
Un método que permitiría evitar al abrazo mortal sin la perdida en la utilización del
sistema sería el siguiente: si se conoce toda la información sobre la utilización de recurso
que va utilizar un proceso se puede encontrar la secuencia de asignaciones que eviten el
abrazo mortal. Así, si sabemos que el proceso P va a utilizar primero ta cinta, después un
archivo y finalmente la impresora, podemos saber si debemos aceptar la petición del proceso
Q que está utilizando la impresora y pide utilizar la cinta.
Existen varios métodos que funcionan de esta manera. Los requerimientos de
información que es necesario conocer previamente varían en cada uno de ellos. El más
simple de ellos necesita que se declaren cuales son las necesidades máximas de cada
proceso. Dada esta información es posible construir un algoritmo que nunca llega a un
estado de abrazo mortal. El algoritmo verifica dinámicamente que nunca exista la condición
de cola circular. Se define el estado del sistema como la asignación de recursos, las
necesidades máximas de ellos y los que hay disponibles. Un estado es seguro si existe una
secuencia de asignación que de los recursos a cada proceso y evite el abrazo mortal.
En forma más precisa se puede decir que un estado es seguro sólo si existe una
secuencia segura. Una secuencia { P I ,P,2........, Pn } es segura para el estado presente de
asignaciones si es posible darle los recursos que necesita al proceso Picon los disponibles y
los que tienen los procesos Pj b'j< i. Es decir, si los que necesita el recurso Pi aún no están
disponibles puede esperar hasta que terminen todos los Pj Una vez que se le han
entregado eventualmente terminará y liberará los recursos que tiene, permitiendo que el
proceso Pi+, tenga todos los recursos que necesita y así sucesivamente.
,
Un estado seguro no es un estado de abrazo mortal. Y un estado de abrazo mortal es
un estado inseguro. Sin embargo, no todos los estados inseguros son estados de abrazo
mortal, un estado inseguro puede llevar a un abrazo mortal.
Abrazos Mortales 15
1.3
1.3.6 Recuperación del abrazo mortal
Cuando se determina que existe un abrazo mortal, existen varias alternativas. Una
posibilidad es informar al operador de lo que ha sucedido y dejar que él lo maneje
manualmente. La otra posibilidad es que el sistema se recupere automáticamente. Hay dos
formas de romper un abrazo mortal:
rn La primera es matar uno o más procesos para romper la cola circular. Una vez que
se decide terminar los procesos se pueden hacer dos cosas. En cualquiera de ellas
los recursos de los procesos terminados son apropiados por el sistema.
0
Matar a todos /os procesos en abrazo mortal. Con ello se eliminará el
abrazo mortal, pero el costo es muy alto. Puede ser que algunos de los
procesos llevase mucho tiempo y necesiten ser vueltos a ejecutarse.
0
Matar un proceso a la vez hasta que se elimine el abrazo mortal. Este
método utiliza mucho tiempo de CPU ya que después de que cada
proceso es matado debe verificar si aún existe el abrazo mortal. Como ya
se señaló el algoritmo de detección es costoso.
0
Inanición. La inanición es un problema que surge al quitarle recursos a
los procesos. Debemos asegurarnos de que no existan procesos a los que
siempre se les quiten los recursos. Si la elección se da en base a un factor
de costo, entonces puede ser que un proceso sea elegido siempre como
víctima. A fin de evitar esto se puede guardar información adicional de
cada proceso como el número de veces que ha sido reiniciado. Y añadir al
costo este factor.
La segunda es apropiarse de un recurso que este en abrazo mortal. Un mejor
esquema puede lograrse combinando la prevención y recuperación. Este se da
gracias a las distintas características de los recursos, algunos que son fáciles de
apropiar, otros que permiten ser ordenados y así. Supongamos que tenemos un
sistema con los siguientes tipos de recursos:
Recursos internos. Recursos usados por el sistema, como el bloque de
control de procesos.
Memoria central. Memoria usada por los procesos.
Recursos de los trabajos. Periféricos asignables como cintas y archivos .
Espacios de intercambio. Espacio en la memoria secundaria para los
procesos.
Con cada tipo se pueden utilizar distintos esquemas como sigue:
Recursos internos. Aquí se puede utilizar prevención utilizando
ordenamiento.
Memoria central. Ya que este es un recurso que se puede apropiar
fácilmente se puede utilizar prevención por apropiación.
0
Recursos de los trabajos. Estos recursos se pueden administrar con el
algoritmo del banquero.
0
Espacio de intercambio. Se puede asignar preasignación ya que es posible
saber el máximo utilizado de antemano.
Así varios esquemas se pueden combinar a fin de obtener una solución efectiva al
problema del abrazo mortal a un bajo costo.
16
Procesos
1.4 Problemas de la Programación Concurrente.
Los ejemplos más ilustrativos en donde se da solución a los problemas más comunes
de la programación concurrente son ( Ben - Aril 1990 ):
I.Mergesort. Ordenación de un arreglo de N entradas, en forma ascendente o
descendente.
0
Solución. Como es un algoritmo secuencial, nosotros ordenamos cada mitad
del arreglo y luego unimos los resultados. Si tenemos un sistema con varios
procesadores, los ordenamientos pueden hacerse en paralelo para obtener
un mejor desempeño.
Para implementar el paralelismo requerimos sincronización y comunicación.
Cada paso de unión debe ser sincronizado con la producción de un nuevo
resultado desde uno de los ordenamientos.
2. Buffer limitado. Hay dos técnicas usadas para limitar el tamaño del buffer.
0
0
Solución 1. La primera es el buffer circular, el cual es como el buffer infinito,
excepto que el indice es calculado modulo el tamaño del arreglo.
Solución 2. Otro método para limitar el tamaño del buffer es hacer notar que
una vez que un dato ha sido usado, este espacio nunca es usado, así que
debemos usarlo en vez de solicitar un nuevo indice.
Para el problema de productor-consumidor con buffer limitado, la
sincronización es necesaria para asegurarnos de que el consumidor puede
ser detenido mientras espera el siguiente buffer que le mande el productor y
el productor puede ser detenido si no hay buffers en el pool.
3. Lectores y Escritores. Es similar a el problema de exclusión mutua en varios
procesos que estan compitiendo para accesar a la sección crítica. En este
problema, sin embargo, dividimos los procesos en dos clases:
Lectores: procesos que no requieren excluir a los demás.
Escritores: procesos los cuales requieren excluir a los otros procesos, ya sean
lectores o escritores.
El problema es una abstracción de acceso a base de datos, donde no hay peligro
en tener varios procesos leyendo al mismo tiempo, pero escribir o cambiar los datos
debe hacerse al mismo tiempo, pero escribir o cambiar los datos debe hacerse bajo
exclusión mutua para asegurar consistencia.
0
Solución. En esta solución, el primer lector que obtiene el acceso a la base
de datos lleva a cabo un down en el semáforo db. Los siguientes lectores
sólo incrementan un contador rc. Al salir los lectores, éstos decrementan el
contador y eJ úJtirno en salir hace un up al semáforo, lo cual permite entrar a
un escritor bloqueado, si es que existe.
Problemas de la Programación Concurrente 17
1.4
Una hipótesis explícita en esta solución es que los lectores tienen prioridad
sobre los escritores. Si surge un escritor mientras varios lectores se
encuentran en la base de datos, el escritor debe esperar. Pero si aparecen
nuevos lectores, de forma que exista al menos que exista al menos un lector
en la base de datos, el escritor deberá esperar hasta que no haya más
lectores interesados en la base de datos. (Tanenbaum, 1992)
4. Filósofos comensales. Este problema se explicará con más detalle en el capítulo 4.
5. Multiplicación matricial. Multiplicación de matrices de N * N.
0
Solución. El proceso i-ésimo (l<i<N) realiza de manera independiente la
multiplicación del renglón i-ésimo por la columna i-ésima. Un proceso N+l
realiza el acomodo de los resultados en la matriz resultante.
6. La suma de un conjunto de N números.
0
0
0
Solución 1: Dos procesos calculan de manera independiente la suma de N/2
números de un conjunto. Un tercer proceso recibe las dos sumas parciales y
calcula la solución final.
Solución 2: Crear un árbol binario de procesos de profundidad d. Este árbol
tiene 2d niveles, cada uno de los cuales calcula la suma de N/Zd números y
pasa el resultado parcial a su padre. Un proceso interior suma los valores
pasados a este por sus hijos. En la raíz proceso la salida del resultado final.
Solución 3: Un conjunto de k procesos tiene acceso a el conjunto entero de
números. Un proceso remueve dos números, los suma y regresa el resultado
a el conjunto. Cuando hay solamente un número en el conjunto, el programa
termina y regresa ese valor.
7. El programa lee registros de 80- caracteres y escribe el dato como registros de 125
caracteres. Un blanco extra es puesto después de cada registro de entrada y un
par de asteriscos (**) es reemplazado por un signo de exclamación (!)..
O
Solución: Usar procesos de árbol, uno para leer y descomponer la entrada,
uno para filtrar los pares de asteriscos y uno para componer los registros de
salida.
8. Cálculo del coeficiente binomial:
(n k) = n (n-I) ...(n-k+l) Ax2 ...k
Solución 1: Un proceso calcula el numerador y un segundo procesos calcula
el denominador. Un tercer proceso hace la división.
18 Procesos
Solución 2: Notar (probar) que i! divide a j(j+l) ...U+ ¡-I). El proceso
numerador puede recibir resultados parciales de el proceso denominador y
hacer la división inmediatamente, guardando los resultados intermedios
desde que llegan a ser grandes también. Por ejemplo: 1x2 divide a 10x9,
1x2~3
divide a 10x9~8,
y así continuamente.
9. Dar dos árboles binarios con niveles etiquetados, verificar si la secuencia de
etiquetas es la misma en cada árbol. Por ejemplo, los dos árboles definidos por la
expresión (a,(b,C)) y ((a,b),C) tienen la misma secuencia de niveles.
0
Solución: Crear dos procesos para recorrer los árboles concurrentemente.
Ellos nos enviaran las hojas etiquetadas en el orden encontrado para un
tercer proceso de comparación.
10. Sean S y T dos conjuntos disjuntos de números donde s y t son dos elementos
elementos en los conjuntos correspondientes. Dar un programa el cuál modifica los
dos conjuntos, tal que S contiene los s miembros más pequeños de SUT y T
contiene los t números más grandes de S UT.
0
Solución 1: El proceso Ps encuentra los elementos más grandes en S y los
envía para el proceso Pt, el cuál encuentra los números más grandes en T y
los envía al proceso Ps.
Solución 2: Crea SUT. Permite a el proceso Ps extraer los s elementos más
pequeños de SUT y Pt extrae los t elementos más grandes de SUT.
11. El Juego de la Vida. Un conjunto de células es arreglado en un arreglo rectangular
(potencialmente finito) tal que cada célula tiene ocho vecinos (horizontalmente,
verticalmente y diagonalmente). Cada célula esta “viva” o “muerta”. Dado un
conjunto finito inicial de células vivas, calcular la configuración obtenida después de
una secuencia de generaciones. Las reglas para del paso de una generación a la
siguientes son:
(a) Si una célula está viva y tiene menos de dos vecinas vivas, ésta muere.
(b) Si ésta tiene dos o tres vecinas vivas, ésta continua con vida.
(c) Si tiene cuatro o más vecinas vivas, ésta muere.
(d) Una célula muerta con exactamente tres vecinas vivas llega a vivir.
Solución: Cada célula es simulada por un proceso. Hay dos problemas a
resolver. Primeramente, el cálculo de la siguiente generación debe ser
sincronizado, por ejemplo, la modificación de cada célula de ser en base a
un estado de las células vecinas en la misma generación. Además, una gran
estructura de datos de procesos y canales de comunicación debe ser creado.
Problemas de la Programación Concurrente 19
1.4
0
...u+
Solución 2: Notar (probar) que i! divide a j(j+l)
¡-I). El proceso
numerador puede recibir resultados parciales de el proceso denominador y
hacer la división inmediatamente, guardando los resultados intermedios
desde que llegan a ser grandes también. Por ejemplo: 1x2 divide a 10x9,
1 x 2 ~ divide
3
a 10x9~8,
y así continuamente.
9. Dar dos árboles binarios con niveles etiquetados, verificar si la secuencia de
etiquetas es la misma en cada árbol. Por ejemplo, los dos árboles definidos por la
expresión (a,(b,C)) y ((a,b),C) tienen la misma secuencia de niveles.
0
Solución: Crear dos procesos para recorrer los árboles concurrentemente.
Ellos nos enviaran las hojas etiquetadas en el orden encontrado para un
tercer proceso de comparación.
I O . Sean S y T dos conjuntos disjuntos de números donde s y t son dos elementos
elementos en los conjuntos correspondientes. Dar un programa el cuál modifica los
dos conjuntos, tal que S contiene los s miembros más pequeños de SwT y T
contiene los t números más grandes de S UT.
Solución 1: El proceso Ps encuentra los elementos más grandes en S y los
envía para el proceso Pt, el cuál encuentra los números más grandes en T y
los envía al proceso Ps.
Solución 2: Crea SUT. Permite a el proceso Ps extraer los s elementos más
pequeños de SUT y Pt extrae los t elementos más grandes de SuT.
11. El Juego de la Vida. Un conjunto de células es arreglado en un arreglo rectangular
(potencialmente finito) tal que cada célula tiene ocho vecinos (horizontalmente,
verticalmente y diagonalmente). Cada célula esta “viva” o “muerta”. Dado un
conjunto finito inicial de células vivas, calcular la configuración obtenida después de
una secuencia de generaciones. Las reglas para del paso de una generación a la
siguientes son:
(a) Si una célula está viva y tiene menos de dos vecinas vivas, ésta muere.
(b) Si ésta tiene dos o tres vecinas vivas, ésta continua con vida.
(c) Si tiene cuatro o más vecinas vivas, ésta muere.
(d) Una célula muerta con exactamente tres vecinas vivas llega a vivir.
0
Solución: Cada célula es simulada por un proceso. Hay dos problemas a
resolver. Primeramente, el cálculo de la siguiente generación debe ser
sincronizado, por ejemplo, la modificación de cada célula de ser en base a
un estado de las células vecinas en la misma generación. Además, una gran
estructura de datos de procesos y canales de comunicación debe ser creado.
20 Procesos
12. El problema es escribir un disco servidor que minimice el arreglo rectangular del
tiempo de búsqueda de un drive de disco. Un sencillo servidor podría satisfacer
requerimientos para datos en orden decreciente de distancia de una posición actual
del brazo. Desafortunadamente, éste disco servidor podría dejar "morir de hambre
(no atender por un periodo largo de tiempo las solicitudes que hace al procesador)
a un requerimiento para datos si los requerimientos llegan también rápidamente.
"
Solución: Mantener dos colas de requerimientos: una para requerimientos de
datos de números de track menores que la posición actual y uno para
requerimientos con números de track mayores. Satisfacer todos los
requerimientos de una cola antes considerando los requerimientos de la otra
cola. Asegurarnos de que un paso de requerimientos para datos de el track
actual no causa Inanición.
0
13. Calcular todos los números primos desde 2 a n. Notar (probar) que si k no es un
primo, este es divisibles por un primo p(k) <= dk + 1.
0
Solución 1: Destinar un proceso para cada número desde k hasta n. Verificar
si k es divisible por cualquier número menor o igual a dk + 1.
0
Solución 2: Destinar procesos para cada k y tener que borrar todos los
múltiplos de k en el conjunto desde 2 hasta n.
0
Solución 3: Destinar un procesos para borrar todos los múltiplos de 2.
Cuando un número se descubre que es primo por todos los procesos
existentes, designa un nuevo proceso para borrar todos los múltiplos de este
primo.
'
0
Solución 4: Dividir el conjunto en i bloques de tamaño n/i. Designar un
proceso para cada bloque. Comenzar calculando los primos en el primer
bloque. Cuando un primo k ha sido calculado, podemos liberar todos los
procesos conteniendo números arriba de k
CAPíTULO 2
Parallel Virtual Machine (PVM)
2.1 Introducción
PVM es un software que permite a una red de computadoras heterogéneas UNIX, ser
usada como una computadora paralela (Manual de Referencia de PVM ). Distintos
problemas computacionales como la ordenación de arreglos, la solución a una suma de N
números, entre otros, pueden ser resueltos utilizando un conjunto de computadoras.
Ei desarrollo de PVM inicia en el verano de I989 en el Oak Riúge National
Laboratory (ORNL) y es ahora un proyecto de investigación en marcha que involucra a las
siguientes Universidades y dependencias norteamericanas' : Universidad de Emory, ORNL,
Universidad de Tennessee, Universidad Carnegie Mellon, Departamento de Energía,
Fundación Nacional de Ciencia, Estado de Tennessee y Centro de Supercómputo de
Pittsburgh. Debido a su naturaleza experimental, el proyecto PVM, produce software que es
utilizado por investigadores de la comunidad científica y otros centros de enseñanza. Este
software es y ha sido distribuido libremente con el interés del avance de la ciencia y es
usado en todo del mundo.
Bajo PVM un usuario define una colección de computadoras con un solo procesador
para simular una gran computadora de memoria distribuida; proporciona las funciones para
iniciar tareas en una máquina virtual2y permite a éstas sincronizarse y comunicarse.
Una tarea se define en PVM como una unidad de cómputo, análoga a un proceso
UNIX. Las aplicaciones en Fortran 77 Ó C pueden ser paralelizadas utilizando código común
de paso de mensajes. Múltiples tareas de una aplicación pueden cooperar para resolver un
problema en paralelo pasando y recibiendo mensajes.
PVM proporciona funciones para explotar mejor la arquitectura de cada computadora
en la solución de problemas, maneja todas las conversiones de datos que pueden ser
requeridas si dos computadoras usan diferentes representaciones de punto flotante ó
enteros. Además, permite a la máquina virtual ser interconectada por una variedad de redes
diferentes.
' http://www.netlib2.cs.utk.edu
* El Término Máquina Virtual es usado para identificar a una computadora 16gica de memoria distribuida.
21
22
Parallel Virtual Machine
El sistema PVM está compuesto por dos partes : La primera parte del sistema la
conforma un demonio3 llamado pvmd3, algunas veces abreviado como pvmd, el cual reside
en todas las máquinas que conforman la máquina virtual. pvmú3 puede ser diseñado por
cualquier usuario que cuente con un login y un password, y que pueda instalar el demonio
en una máquina. Cuando el usuario va a ejecutar una aplicación, debe crear una máquina
virtual para iniciar PVM, las aplicaciones pueden ser ejecutadas desde el shell de UNIX. La
segunda parte del sistema la conforma una biblioteca de rutinas de interfaz PVM, ubicada
dentro del archivo libpvm3.q la cual contiene rutinas de paso de mensajes, creación de
procesos, coordinación de tareas, y modificación de la máquina virtual. Los programas de
aplicación deben ser ligados con esta biblioteca para poder utilizar PVM.
2.2 Obteniendo e Instalando PVM.
PVM no requiere de privilegios especiales para ser instalado. Cualquier persona que
posea un login puede hacerlo. PVM-ARCH es usada a lo largo de este reporte para
representar el nombre de la arquitectura que PVM usa para una computadora dada. La
siguiente tabla muestra todos los nombres de PVM-ARCH y los correspondientes tipos de
arquli
TABLA 2.1
Son procesos que siempre se están ejecutando. ( W. Richard Stevens, 1996 )
Obteniendo e Instalando PVM 23
2.2
Hay varias formas de obtener el software y la documentación. La guía de usuario, el
código fuente de PVM 3, las páginas de ayuda, XPVM, y apuntadores a otros paquetes
PVM relacionados, se encuentran disponibles en netlib. Netlib es un servicio de distribución
de software ubicado en Internet. Hay varias maneras de obtener software de netlib.
La primera es con una herramienta llamada xnetlib. Xnetlib es una interface X
Windows que permite al usuario navegar y solicitar la transferencia de PVM a la
computadora del usuario. Para obtener xneflib envíe un e-mail a [email protected] con el
mensaje send xnetlib.shar from xnetlib o un ftp anónimo a cs.utk.edu pub/xnetlib.
Los archivos netlib pueden también ser obtenidos por un ftp anónimo a
netlib2.cs.utk.edu. Ubicándose en el directorio pvm3. El archivo índex describe los archivos
en este directorio.
El software PVM puede ser solicitado por e-mail. Para recibir este software enviar un
e-mail a [email protected] con el mensaje: send índex from pvm3. Un manejador
automático de correo electrónico regresará una lista de los archivos y demás instrucciones
adicionales por e-mail. La ventaja de este método es que cualquier persona con acceso email a Internet puede obtener el software.
Los archivos de código fuente, que consumen alrededor de un Megabyte cuando se
desempacan, están disponibles en el formato tar uudecodedcompresed.
Para instalar PVM, coloque el archivo pvm3.3.ü.far.z.u~
en el directorio donde usted
quiere que resida el código fuente. Por default PVM asume que éste es instalado en su
directorio $HOME/pvm3, pero este puede ser instalado en una área más centralizada como
/usr/local/pvm3. Para desempacar el código fuente siga los siguientes pasos:
% uudecode pvm3.3.0.tar.z.u~
% u n c o m p r e s s pvm3.3.0.tar.Z
% tar xvf pvm3.3.0.tar.
PVM usa dos variables de ambiente PVM-ARCH y PVM-ROOT que se colocan en el
archivo .cshrc. Aquí damos un ejemplo para PVM-ROOT:
sefenv PVM-ROOT /HOMUmsr/u2/kohl/pvm3
El método recomendado
para colocar PVM-ARCH,
es anexar la línea
PVM-ROOT//i6/cshrc.sfub en el archivo .cshrc. La línea anterior debe ser anexada después
de la declaración de la variable de ambiente PATH. Este archivo automáticamente determina
PVM-ARCH para éste host y es particularmente Útil cuando el usuario comparte un
fi/esystem4 común ( tal como NFS, Network File System ) a través de arquitecturas
diferentes.
4
Es una parte del Arbol de archivos UNIX, que consiste en un directorio, subdirectonos y archivos ( Nemeth, 1996).
24 Parallel Virtual Machine
El código fuente de PVM se obtiene con directorios y makef7/es5 para distintas
arquitecturas. La construcción para cada tipo de arquitectura se realiza automáticamente
tecleando: make dentro del directorio PVM-ROOT. El makefile determina automáticamente
libfp~m3.a~
pvmgs y
en cuál arquitectura se está ejecutando y construirá pvmd3, libp~m3.a~
libgpvm3.a. Éste coloca todos los archivos en pvm3Aib/PVM_ARCH con excepción de
pvmgs, el cual es colocado en PVM-ROOT/bin/PVM-ARCH.
PVM busca los archivos ejecutables del usuario en la dirección default
$HOM€(pvm3/bin/PVM_ARCH. Si PVM es instalado en otra dirección como /usrAoca/ para
todos los usuarios, entonces cada usuario podría también crear el directorio
$HOM€(pvrn3/binn'VM_ARCH para colocar sus propios ejecutables. Por ejemplo: si una
aplicación de usuario PVM espera producir una tarea llamada Tarea1 en una estación
SPARC
llamada tlallocl, entonces en tlallocl tendría que haber un archivo ejecutable
$ H O M € ( p v r n 3 / b i n / ~ U r e a1. Este default puede ser cambiado para un path distinto en
el hostfi/e. Ver Sección 2.3.2.
2.3 Configuración de la Máquina virtual
2.3.1 La Consola PVM.
La consola PVM llamada pvm, es la Única tarea PVM, que permite al usuario iniciar,
preguntar y modificar el estado de la máquina virtual iterativamente. La consola puede iniciar
y detener su ejecución múltiples veces en cualquiera de los host que conforman la máquina
virtual, sin afectar a PVM o a cualquier aplicación que pueda estar ejecutándose.
Cuando inicia la consola pvm, comprueba si PVM ya se está ejecutando, y si no
automáticamente ejecuta pvmd en este host, pasando a pvmd las opciones de la línea de
comandos y el archivo hostfile. De esta forma, PVM no necesita estar ejecutándose para
que se pueda inicializar la consola.
pvm bd cdebugmask>j[hoMIej
pvm -n<hostname>
La opción -d habilita la máscara de depuración ( número hexadecimal que
corresponde a los bits de depuración de pvmd.c ) .
~~
SArchivoque ejecuta el comando make. Ver manual de UNIX,
Ver tabla 2.1.
6
Configuración de la Máquina Virtual 25
2.3
La opción -n es utilizada para especificar un nombre alterno para el pvmd principal (en
caso de que el hostname' no sea igual a la dirección IP que se desea). Ésta opción es útil si
el host tiene múltiples redes conectadas a él, tales como FDDl o ATM, y se desea que PVM
use una red en particular.
Una vez inicializada la consola, imprime el prompt:
y acepta comandos desde la entrada estándar (teclado).
Los comandos disponibles en la consola son:
add Seguido por uno o más nombres de hosts, agrega éstos a la máquina virtual.
alias Define o lista los alias de comandos.
conf Lista la configuración de la máquina virtual, incluyendo nombre del host,
identiificador de la tarea pvmd y tipo de arquitectura.
delete Seguido por uno o más nombres de hosts, los elimina de la máquina virtual.
Los procesos PVM ejecutándose en los hosts eliminados son perdidos.
echo Repite argumentos.
halt Termina todos los procesos PVM incluyendo la consola y los demonios,
deteniendo la ejecución de PVM.
help Puede ser usado para obtener información acerca de cualquiera de los
comandos de la consola. La opción help seguida por un comando de la consola pvm,
lista todas las opciones y banderas disponibles para éste comando.
id Imprime en la consola el ID de la tarea.
jobs Lista los procesos que se ejecutan en la máquina virtual.
kill Termina cualquier proceso PVM.
mstat Muestra el estado de los host especificados.
ps -a Lista todos los procesos que se encuentran actualmente en la máquina virtual,
sus direcciones, sus ID's de tareas, y los ID's de sus tareas padre.
pstat Muestra el estado de un sólo proceso PVM .
quit Sale de la consola dejando a los demonios y tareas PVM en ejecución.
7
Cada computadora en la red debe de taner un nombre unico.
26 Parallel Virtual Machine
reset Elimina todos los procesos PVM excepto la consola, reinicia todas las tablas
internas y colas de mensajes PVM. Los demonios son dejados en un estado
estacionario' .
setenv Despliega o coloca variables de ambiente.
sig Seguido por un número de señal
correspondiente.
y tid, envía la señal a la tarea con el tid
spawn lnicializa una aplicación PVM. Las opciones incluyen:
-count Número de tareas, por default 1.
-(host) Se produce en un host , por default cualquiera.
-(PVM-ARCH) Se produce en un hosts de tipo PVM-ARCH.
-? Habilita depuración.
- > Redirecciona la salida de una tarea a la consola.
- > file Redirecciona la salida de una tarea a un archivo especificado
en file.
->>fi/e Redirecciona la salida de una tarea anexándola a un archivo
especificado en file.
unalias Deja de definir un alias de comando.
version Imprime la versión de libpvm que es usada.
La consola lee el archivo $HOME/.pvmrc antes de leer comandos desde el tty 'O , así
puede hacer cosas como:
alias ? help
alias h help
alias j jobs
setenv PVM-EXPORT DISPLAY
$ imprime mi id
echo nuevo shell pvm
id
Los dos métodos más comunes para ejecutar PVM3 son: iniciar la consola pvm y
agregar hosts manualmente (pvm también acepta un argumento de nombre de host
opcional), ó iniciar pvmd con un hosffi/e.
2.3.2 El archivo hostfile.
El archivo hosffile define la configuración inicial de los hosts, que PVM combina para
crear la máquina virtual. Este archivo también contiene información acerca de los hosts que
el usuario desee agregar posteriormente a la configuración.
* Ver Sección 1.1.1
'O
ver figura 5.2 de (Nemeth, 1996).
tipo de terminal
Configuración de la Máquina Virtual 27
2.3
Cada usuario de PVM podrá tener su propio hostfile, el cuál describe su propia
máquina virtual personal.
El archivo hostfile, en su forma más sencilla, es sólo una lista de nombres de hosts,
un nombre por línea. Las líneas en blanco son ignoradas, y las líneas que comienzan con un
# son líneas de comentario.
Estos símbolos ( # ) permiten al usuario comentar su hostfile y también proporcionar
una forma rápida de modificar la configuración inicial.
Un ejemplo de una configuración, se ve a continuación:
# mi primer configuración
tlalloc4
tlalloc2
xanum.uam.mx
alpha.cs.cinvestav.mx
xhara2. uam.mx
hermes.cs.uh.edu
Las máquinas que pertenecen a la misma red local, no es necesario especificar su
dirección completa.
Existen varias opciones que pueden ser especificadas en cada línea después del
nombre del host. Las opciones son separadas por un espacio en blanco.
Opciones:
lo = userid Permite al usuario especificar un login alternativo para este host; de otra
manera, se usará el login inicial de la máquina.
so = pw Causará que PVM pida al usuario accese a este host con su password.
Esto es útil en el caso donde el usuario tenga un UID" y un password distinto en el sistema
remoto. PVM usa rsh por default para inicializar demonios pvmd remotos, pero cuando pw es
especificado, PVM usará rexec() en vez de rsh.
dx = location-of-pvmd
Permitirá al usuario especificar una dirección distinta a la
default de pvmd para este host. Este es Útil si alguien va a usar su propia copia personal de
pvmd.
ep = paths-to-user-executables Esta opción permite al usuario especificar una
serie de rutas de acceso para buscar sus archivos ejecutables. Múltiples rutas son
separados por dos puntos. Si ep= no es especificado, entonces PVM busca las tareas de
aplicación en $HOMElpvm3/binlPVM-ARCH.
sp = value Especifica la velocidad relativa del host comparada con otro en la
configuración. El rango de posibles valores es 1 a 1'000,000 con 1 O00 como default.
11
numero entero que identifica un usuario y es asignado por el root.
28 Parallel Virtual Machine
bx = locationof-debugger Especifica cual es el script’* de depuración que se
invocará en este host si la depuración es requerida en la rutina spawn. Por default el
depurador está en pvrn3Aib/debugger 13.
wd = workingdirectory Especifica un directorio de trabajo en el cual todas las
tareas producidas en este hosf se ejecutarán. El default es $HOME.
so
= ms14 Especifica que el usuario iniciará manualmente un pvmd esclavo en este
host. Es útil si los servicios de red rsh y rexec() están deshabilitados, pero existe
conectividad IP. Cuando usamos esta opción se verá en el tty de pvmd3:
[t80040000] ready Fri Oct 17 78:47:47 7997
**** Manual startup ****
Login to “honk” and type:
pvm3/lb/pvmd -s -do -nhonk I 80a9ca95:0cb6 4096 2 80a95c43:OOOO
Type response:
En honk15después de teclear la línea dada, se observa:
ddpro<23I2>arch<ALPHA>ip<80a95~43:0a8e>mtu<4096>el cuál se transmitirá para
el pvmd principal. En este punto verá:
Thanks
y los dos pvmds están listos para comunicarse.
Si el usuario desea colocar cualquiera de las opciones arriba citadas como default
para un conjunto de hosts, entonces puede colocar estas opciones en una línea con un * al
principio. Las opciones defaults tendrán efecto para todos los hosts siguientes hasta que
haya otra línea default.
Los host que el usuario no quiere agregar en la configuración inicial, pero que serán
utilizados posteriormente, pueden ser especificados en el hosM/e poniendo al principio de
estas líneas un &.
Un ejemplo del desplegado de hostfile y más de estas opciones son mostradas a
continuación:
~
12
archivo que contiene un conjunto de instrucciones que son ejecutadas por el shell.
l 3 La variable de ambiente PW-DEBUGGER puede ser declarada en lugar de bx.
l 4 manual startup (ms).
l 5 honk es el nombre de una máquina.
.-
Configuración de la Máquina Virtual 29
2.3
Hostfile.
# Las líneas de comentarios son iniciadas con #
# Las lineas en blanco son ignoradas
gstws
ipsc dx = /usr/geistlpvrn3/lib/l860/pvmd3
ibmi.scri.fsu.edu lo = gst so = pw
# Las opciones default son colocadas con * para los siguientes host
* ep = $sun/probleml : -/nla/mathlib
sparky
# azure.epm.ornl.gov
midnight.epm.ornl.gov
# reemplaza las opciones default con los nuevos valores para los host
* lo = gageist so = pw ep = problem1
thud.cs.utk.edu
speedy.cs.utk.edu
# Las máquinas por agregar posteriormente, son especificadas con &
# estas sólo necesitan listarse si se requieren.
& sun4 ep = problem1
& castor dr = /usr/local/bin/pvmd3
& dasher.cs.utk.edu lo = gageist
& elvis dr = -/pvmYlib/SUN4/pvmd3
2.3.3 Variables de ambiente
PVM usa dos variables de ambiente cuando se inicializa y se ejecuta. Cada usuario
PVM necesita utilizar estas dos variables para poder disponer de PVM. La primera es
PVM-ROOT, la cual es establecida para la localización del directorio pvm3. La segunda es
PVM ARCH, la cual informa a PVM la arquitectura del host, y de esta manera, decide que
archivos ejecutables debe escoger del directorio PVM-ROOT.
PVM mejora el uso del ambiente y lo mantiene en cada una de los hosts, donde éste
sea distinto. Por ahora esto permite a una tarea exportar cualquier parte de su ambiente a
sus tareas hijo colocando PVM-EXPORT a las variables que serán exportadas a través de
spawnI6
Por ejemplo:
PVM-EXPORT = D1SPLAY:SHELL
exporta las variables DISPLAY y SHELL a las tareas hijo.
l6 (Ver Sección
2.3.1.)
___
30 Parallel Virtual Machine
2.3.4 Problemas al iniciar PVM.
Si PVM tiene un problema al iniciar, desplegará un mensaje de error ya sea en la
pantalla o en el archivo /tmp/pvm/.<uid>. Esta sección tiene como objetivo, ayudar a la
interpretación de los mensajes de error y explicar cómo resolver estos problemas.
Si el mensaje dice:
ft80040000]Can Y start pvmd
Las posibles razones son:
0
0
0
que tu archivo host, en el hosf remoto no contenga el nombre del host desde el
cual tu estás iniciando PVM.
no tener PVM instalado en un host.
no tener PVM-ROOT bien declarado en el mismo host
Para confirmar que el archivo .hots esta colocado correctamente en la máquina
remota, teclea:
% rsh remote.host ‘Is’
Para verificar el último punto teclee:
% rsh remotehost ‘prinfenv ’
Si PVM es eliminado manualmente o detenido anormalmente ( por ejemplo con la
caída del sistema ) entonces, verifica que exista el archivo /tmp/pvmd.<uiá>. Este archivo es
usado para autentificar y existe solamente cuando PVM se esta ejecutando.
Si obtienes un mensaje que dice:
[fB00400000]L ogh Jncorrect
Entonces probablemente significa que no hay una cuenta en la máquina remota con
tu login. Si hay un login distinto en la máquina remota, se puede fijar el otro con la opción:
/o=opfion en el hosffile.
Si obtienes cualquier mensaje desconocido, entonces checa el archivo .cshrc. Es
importante que el usuario no tenga cualquier WS en el archivo .cshrc porque interferirá con la
inicialización de PVM.
Limitaciones de Recursos 31
2.4
2.4. Limitaciones de recursos.
Las limitaciones de recursos impuestas por el Sistema Operativo y el hardware son las
misma para las aplicaciones PVM. Siempre que sea posible, PVM evita colocar limites
explícitos, en vez de esto regresa un mensaje de error cuando los recursos se han
terminado. Naturalmente, la competencia entre usuarios en el mismo host y en la red afectan
algunos límites dinámicamente.
2.4.1 En el demonio PVM.
El número de tareas que cada pvmd puede manejar está limitado por dos factores: el
número de procesos ermitidos a cada usuario por el Sistema Operativo, y el número de
archivos descriptoresI?
disponibles en el pvmd.
El pvmd puede llegar a un embotellamiento si todas las tareas tratan de comunicarse
unas con otras, a través de él.
El pvmd usa asignación de memoria dinámica para almacenar paquetes, en la ruta
entre tareas, hasta que la tarea receptora acepta los paquetes estos son acumulados por el
pvmd en una cola de Primeras Entradas, Primeras Salidas (First lnput First Output, FIFO). El
control de flujo no es impuesto por el pvmd, éste almacenará todos los paquetes dados a él,
hasta que no pueda obtener más memoria.
2.4.2 En la Tarea
Igual que pvmd, una tarea tiene un límite en el número de tareas con las que se
puede conectar directamente. Cada una de las rutas directas a una tarea tienen conexiones
TCP (bidireccionales), y consume cada una un descriptor de archivo. Así con un límite de 64
archivos abiertos, una tarea puede establecer conexiones directas con otras 60 tareas. Este
límite únicamente tiene efecto cuando usamos ruteo directo tarea-tarea. Los mensajes
direccionados vía los pvmds, solamente usan la conexión default pvmd-tarea.
El tamaño máximo de un mensaje PVM está limitado por la cantidad de memoria
disponible para la tarea. Los mensajes generalmente son empaquetados utilizando datos
existentes en la memoria, y éstos pueden residir en ella mientras son empaquetados y
enviados. El mensaje más grande posible que una tarea puede enviar debe ser menor que la
mitad de la memoria disponible.
Si un número no determinado de tareas es enviado a un sólo destino, todas al mismo
tiempo, la tarea destino Ó el pvmd pueden ser sobrecargados, porque éstos tratan de
almacenar los mensajes.
17
Estructuras de datos que guardan información del proceso.
32 Parallel Virtual Machine
2.5 Detalles de Implantación.
2.5.1 Introducción
En ésta sección describimos los detalles de implantación de PVM en una máquina de
un sólo CPU.
El sistema PVM es portable a cualquier versión de UNIX, y a las MPPs ( Message
Passing Machines Whit Many Processors, Máquinas de Paso de Mensajes con Muchos
Procesadores).
PVM permite la construcción de aplicaciones con tolerancia a fallas. A fin de mantener
a PVM tan portable como sea posible, se evitó el uso de características de los sistemas
operativos y de los lenguajes de programación.
Se asume que los sockets” están disponibles para la comunicación entre procesos y
que cada host de la máquina virtual puede conectarse directamente a cualquier otro host
usando protocolos IP ( TCP y UDP ). Esto es, el demonio pvmd debe ser capaz de enviar un
paquete a otro demonio pvmd en un sólo paso. Los requerimientos para la conectividad
completa del IP pueden ser eliminados especificando las rutas y ésto permite a los demonios
pvmd mandar mensajes. Note que algunas máquinas MPPs no tienen disponibles los
sockets en el procesamiento de nodos, pero los tienen en el front-end . (donde el pvmd se
ejecuta).
’’
PVM proporciona rutinas que permiten a los procesos llegar a ser tareas PVM y
volver a ser procesos normales nuevamente. Estas son rutinas para adicionar y eliminar
hosts de la máquina virtual, para iniciar y terminar tareas PVM, para enviar señales a otras
tareas PVM, para obtener información acerca de la configuración de la máquina virtual y de
las tareas que se están ejecutando actualmente bajo PVM .
2.5.2 Características de PVM
2.5.2.1 ldentificador de tareas
Todas las tareas que corren bajo PVM son representados por un identificador de
tareas ( task identifier, tid ), éste es utilizado para la identificación de procesos, cada tarea
tiene un valor único el cual es proporcionado por el demonio pvmd local. PVM contiene
varias rutinas que regresan el valor del tid, de tal forma que se puedan identificar las tareas
en el sistema. Éstas rutinas son pvm-myfjd(), pvm-spawn(), pvm-parent(), pvm-bufinfo(),
pvm-tasks(), pvm-tidtohost() y pvm-gettid().
18
l9
Crea un punto terminal para conectarse a un canal y devuelve un descriptor. ( Márquez,l993)
Seccidn de entrada.
2.5
Detalles de Implentación 33
El tid es un entero de 32 bits, para direccionar demonios pvmd y grupos de tareas en
una máquina virtual. El tid identifica a una Única tarea en la máquina virtual, sin embargo los
tids son reciclados cuando no son usados por largo tiempo.
El tid contiene cuatro campos como se muestra en la figura 2.1, sin embargo el
tamaño de los campos puede cambiar ( de acuerdo a la configuración de la máquina virtual
). Puesto que el tid contiene mucha información, está diseñado para colocar dentro de él un
tipo de datos entero más grande.
31
31 3029
SG
16
24
8
17
H
O
O
L
FIGURA 2.1
El campo L abarca los primeros 18 bits. El campo H los siguientes 12 y los campos G
y S los dos ultimos. Los campos SIG y H tienen significado global, esto es, cada pvmd de
una máquina virtual los interpreta de la misma forma. El campo H contiene el número de
host relativo a la máquina virtual. Cuando la máquina inicia, cada pvmd es configurado con
un número de host único, distinto de cero, partiendo del espacio de dirección de la máquina.
El pvmd con número cero en el campo HIes usado dependiendo del contexto, ya sea para
diferenciar al demonio local pvmd ó a la copia del demonio local llamado pvmd del demonio
maestro pvmd. El número máximo de hosts en la máquina virtual está limitado a 2H -1. La
relación entre el número de host y cada host, es conocido por todos los demonios.
Los mensajes son direccionados al demonio pvmd colocando el bit S y el campo del
host, y llenando de ceros el campo L. En las versiones posteriores de PVM, éste bit debería
de ser cedido al campo H Ó L.
Cada pvmd asigna un significado local al campo L ( cuando el campo H es igual a su
propio número de host ). El demonio pvmd contiene un mapeo entre el valor L y el
ldentificador de procesos UNIX. De la misma forma que el número de hosts, el número de
tareas por host, está limitado de acuerdo al tamaño de su campo TID. Puesto que el campo
L permite 18 bits, a lo más 264143 tareas pueden existir concurrentemente en un host.
En puertos multiprocesadores el campo L es frecuentemente subdividido, por ejemplo,
en un campo partición (P), un campo de número de nodo (N) y el bit de localización W.
Como se muestra en la Figura 2.2
31
24
31 30 29
SG
16
18 17 16
H
w
O
8
10
P
FIGURA 2.2
N
O
34 Parallel Virtual Machine
El campo N abarca los primeros 11 bits. El campo P los siguientes 6 , W el 17, H del
18 al 29 y los campos G y S los dos ultimos.
El campo P especifica una partición de la máquina (algunas veces llamada “tipo de
proceso” Ó “trabajo”), en el caso donde pvmú puede manejar múltiples particiones MPP. El
campo N determina un nodo de CPU específico en una partición. El bit W indica si una tarea
es ejecutada en un nodo MPP Ó en un procesador del host (servicio de nodo). La colocación
del bit W puede ser determinada por la salida de ps -a desde la consola de pvm. Puesto que
la salida de tid en ps, es un número hexadecimal, el quinto dígito de la derecha contiene el
bit W. La siguiente es una tabla de estados para determinar si el bit W es cero o uno.
bit W
tarea corriendo en:
contenido del
O
I
nodo de computadora MPP
nodo de un host
0,1,4,5,8,9,C,d
2,3,6,7,a,b,e,f
!jth
dígito
del tid
Por ejemplo, si el tid es 60001, entonces sabes que tu tarea es ejecutada en el nodo
de una MPP.
Los campos del fiú se conforman como sigue:
S
G
H
L
Naturalmente, los TID’s son ocultados por la aplicación y el programador
deberá intentar predecir sus valores o modificarlos.
no
2.5.2.2 Tolerancia a fallas
Si un host falla, PVM automáticamente lo detectara y lo dará de baja de la máquina
virtual; el estado de los host puede ser solicitado por la aplicación, y si se requiere, un host
de reemplazo puede ser anexado por la aplicación. También es responsabilidad del
desarrollador de aplicaciones, hacer éstas tolerantes a las fallas de los hosts. PVM no
intenta recobrar tareas que hallan sido terminadas por la falla de un host. Otro uso de la
tolerancia a fallas, es la de agregar más hosts cuando sea necesario; por ejemplo en un fin
de semana cuando el nivel de actividad de una institución disminuye, la aplicación puede
adicionar más host para aumentar su poder de cómputo.
2.5
Detalles de Implentación 35
2.5.2.3 SeñaIización
PVM proporciona dos métodos para el envío de señales entre distintas tareas PVM.
0
0
El primero consiste en enviar una señal UNIX.
El segundo, notifica a una tarea acerca de un evento por medio de un mensaje,
este tiene una etiqueta para un usuario específico. Hay varios eventos que se
pueden notificar a las tareas en PVM, entre ellos están: la finalización de una
tarea, la perdida o falla de un host, y la adición de un host.
2.5.2.4 Comunicación
PVM proporciona rutinas para empaquetar y enviar mensajes entre tareas.
El modelo asume que:
0 cualquier tarea puede enviar un mensaje a cualquier otra.
0 no hay un límite para el tamaño.
0 no hay número máximo de tales mensajes.
En todos los hosts hay limitaciones físicas de memoria, lo cual limita el espacio del
buffer, el modelo no se restringe a las limitaciones de una máquina en particular y asume
que dispone de memoria suficiente. Ésto permite enviar bloques asincrónicamente, recibirlos
del mismo modo y funciones para recibir no-bloques. Hay opciones en PVM 3 que requieren
que los datos sean transferidos directamente de una tarea a otra. En este caso, si el
mensaje es extenso, el emisor puede retener el mensaje hasta que el receptor este listo
para recibirlo.
Un no-bloque recibido, inmediatamente regresa ya sea con el dato ó con la bandera
de que el dato no llegó, mientras que un bloque recibido regresa sólo cuando el dato está en
el buffer de recepción. Una rutina puede ser llamada para que retorne información acerca de
los mensajes recibidos.
El modelo PVM garantiza que el órden de los mensajes se preserva. Si la tarea 1
envía un mensaje A a la tarea 2, después la tarea 1 envía un mensaje B a la tarea 2, el
mensaje A llega a la tarea 2 antes que el mensaje B.
2.6 El Demonio PVM
Una vez que el pvmd es ejecutado en cada host de la máquina virtual y los pvmds son
configurados para trabajar juntos. Los pvmds de un usuario no interactúan con los de otros.
El pvmd fue diseñado para ejecutarse bajo un UID no privilegiado que ayude al usuario a
reducir los riesgos de seguridad y para minimizar el impacto de un usuario PVM con los
demás.
El demonio pvmd sirve como ruteador y controlador de mensajes. Esto proporciona un
punto de contacto entre cada uno de los hosts, así como autentificación, control de procesos
y detección de fallas. Los demonios inactivos, ocasionalmente mandan un mensaje a los
36 Parallel Virtual Machine
demás demonios, para verificar su alcance; cuando un pvmd no responde es marcado como
deteriorado. Los pvmds son más resistentes que los componentes de una aplicación y
continúan ejecutándose si el programa es interrumpido, facilitando la depuración.
El primer pvmd ( iniciado manualmente ) es llamado el pvmd maestro, mientras los
otros ( iniciados por el maestro ) son llamados pvmds esclavos. En la mayoría de las
operaciones, todos los pvmds son considerados iguales. Únicamente el maestro puede
iniciar nuevos pvmds esclavos y adicionarlos a la configuración de la máquina virtual. De la
misma forma, sólo el maestro puede eliminar esclavos de la máquina virtual. Si el pvmd
maestro pierde contacto con un esclavo, éste marca al esclavo como dañado y lo suprime
de la configuración. Si un esclavo pvmd pierde contacto con el maestro, el esclavo por sí
mismo se dará de baja. Este algoritmo asegura que la máquina virtual no llegue a ser
particionada y continué ejecutándose como dos máquinas virtuales. No hay actualmente
forma de que el maestro deje sus funciones a otro pvmd, así que éste es siempre parte de la
configuración.
Las estructuras de datos más importantes son las tablas de hosts y las tablas de
tareas, las cuales describen la configuración de la máquina virtual y la localización de las
tareas ejecutándose bajo pvmd. Además de ellas están las colas de mensajes y paquetes, y
contextos de espera para almacenar información del estado de las multitareas en el pvmd.
Cuando se inicia el pvmd se configura así mismo, ya sea como un maestro ó como
un esclavo, dependiendo de sus argumento en la línea de comandos. AI momento de que
pvmd crea y liga sockets para comunicarse con las tareas y con otros pvmds, abre un
archivo de errores e inicializa las tablas. Para un pvmd maestro, la configuración puede
incluir lectura del hostfile y determinación de parámetros default, tales como el nombre del
host (hostname). Un pvmd esclavo obtiene sus parámetros de la línea de comandos y envía
una línea de datos de regreso al proceso inicial para incluirse en la tabla de hosts.
Después de configurarse así mismo el pvmd entra en un ciclo en la función work(). El
corazón del ciclo work() es una llamada a select() que prueba todos los recursos de entrada
para el pvmd ( tareas locales y la red ). Los paquetes que llegan son recibidos y enrutados a
sus destinos. Los mensajes direccionados a pvmd son redireccionados a las funciones
loclentry(), netentry() Ó schedentry().
2.6.1 Inicio de pvmd
AI adicionar un nuevo host a la máquina virtual, nuestro principal objetivo es obtener
un pvmd ejecutándose con bastante información (por ejemplo, el identificador del pvmd
maestro) para configurarlo completamente como los anteriores.
Varios mecanismos están disponibles para iniciar un pvmd dependiendo del sistema
operativo. Naturalmente nosotros queremos usar un método que sea disponible, seguro,
rápido y fácil de instalar. Queremos evitar teclear passwords todo el tiempo, pero no
queremos ponerlos en un archivo donde éstos puedan ser descubiertos. Los sistemas
operativos no conocen todos estos criterios. Inetd2' daría rapidez, inicialización confiable,
2o Arquitectura de
red.
El DemonioPVM 37
2.6
pero requeriría que el administrador del sistema instalara PVM en cada uno de los hosts
donde sería usado. lnicializar el pvmd con rlogin o telnet permitirá accesar a cualquier host
con servicios rsh o conexión IP, no requeriendo privilegios especiales para ser instalado. La
principal desventaja es el esfuerzo que se requiere para obtener un programa que se
comunique y un script que trabaje confiablemente. Dos sistemas ampliamente disponibles
son rsh y rexeco. Nosotros usamos ambos para cubrir muchas de las características
requeridas.
rsh es un programa, el cual puede ser usado por el pvmd para ejecutar comandos en
un host remoto sin un password. Esto puede ser hecho colocando un password común en
los hosts (requiriendo los servicios del administrador del sistema) o creando un archivo .rhost
en el host remoto. El uso de rsh es frecuentemente impedido por tener un alto riesgo de
seguridad, su uso es restringido al archivo .rhost. La alternativa rexec(1, es una función
compilada dentro de pvmd diferente a rsh la cual no requiere password; rexecg requiere que
el usuario proporcione uno a tiempo de ejecución, ya sea tecleando éste o colocándolo en un
archivo .netrc (esto es, en realidad una mala idea).
Cuando el pvmd maestro recibe un mensaje DM-ADD, éste crea una nueva entrada
en la tabla de hosts. Las descripciones del host son guardadas en una estructura waitc-add
ligada a un contexto de espera, y aún no adicionado a la tabla de hosts. Entonces crea una
copia de pvmd ( pvmd‘ ), la cual hace el trabajo rudimentario, pasando a éste una lista de
hosts y comandos a ejecutar.
Cualquiera de los pasos en el proceso de inicio ( por ejemplo obtener la dirección IP
del host, comenzar un shell, etc. ) pueden bloquear la ejecución por segundos o minutos, y
el maestro pvmd debe ser capaz de responder a otros mensajes durante este tiempo. El
pvmd’ tiene el número de host cero y se comunica con el pvmd maestro a través del
protocolo pvmd-pvmd, aunque éste nunca se comunica con los pvmds esclavos. La
operación de inicio tiene un contexto de espera en el pvmd maestro. En caso de interrupción
del pvmd’, el pvmd maestro recibe una señal SIGCHLD, entonces llama a hostfailentry(),
para dar de baja a pvmd‘.
pvmd’ usa rsh o rexec() ( o inicialización manual ) para comenzar un pvmd en cada
host nuevo, pasa parámetros al pvmd y obtiene una línea de información sobre la
configuración. Cuando finaliza pvmd’ envía un mensaje DM-STARTACK de regreso al pvmd
maestro; que contiene las líneas de configuración o los mensajes de error. El maestro
analiza el resultado y completa los descriptores de host almacenados en el contexto de
espera. Los resultados son regresados en un mensaje DM-ADDACK. Los hosts iniciados
son configurados en la máquina usando el protocolo de actualización de tabla ( DM-HTUPD
). El diálogo de configuración entre pvmd’ y un nuevo esclavo es similar a lo siguiente:
pvmd -> slave:
[exec/ $PVM-ROO?Xb@md -s -dd-honkf dOa9w950f5a4096 3 dOa95c430000
slave ->pvrndI’
ddpro ~2312,
arch <ALPHA>ip<dOa95~~Ob3f>mfnc4096>
pvmd-> slave:
€OF
38 Parallel Virtual Machine
Los parámetros del pvmd maestro ( máscara de depuración, índice en la tabla de
hosts, direcciones IP y MTU ) y el esclavo ( nombre del host, índice en la tabla de hosts y
dirección IP ) son pasadas en la línea de comandos. El esclavo responde con su
configuración ( número de revisión del protocolo pvmd-pvmd, arquitectura del host,
direcciones IP y MTU), espera un EOF del pvmd’ y se desconecta del pipe 27 , poniéndose en
un estado de ejecución de prueba (runstate = PVMDSTARTUP). Si recibe el resto de la
información ( de la configuración ) desde el pvmd maestro, fuera de tiempo ( DDBAILTIME
por default cinco minutos ) inicia su ejecución normal. De otra manera, este asume que hay
algunos problemas con el maestro y termina.
Si una tarea especial llamada hoster ”, tiene registrada con el pvmd maestro una
prioridad para recibir los requerimientos DM-ADD, el sistema de inicialización normal no es
usado. En lugar de bifurcar el pvmd’ un mensaje SM-STHOST es enviado a la tarea del “
hoster ”. Esto debe iniciar el proceso remoto como se describe arriba (usando cualquier
mecanismo si éste quiere), paso de parámetros y colección de respuestas, entonces envía
un mensaje SM-STHOSTACK devuelta a pvmd. Si la tarea del hoster falla durante una
operación de adición, el pvmd usa el contexto de espera para recuperarse. Este asume que
ninguno de los procesos fueron iniciados y envía un mensaje DM-ADDACK indicando un
error del sistema.
I‘
2.6.2 Tabla de hosts
La tabla de hosts es una estructura de datos y describe la configuración de la máquina
virtual. Hay una tabla por cada demonio pvmd y son sincronizadas a través de todos los
pvmds. La configuración de la máquina puede decaer durante un tiempo como hosts caigan
Ó sus redes lleguen a estar desconectadas.
Las tablas de hosts de los pvmds esclavos son modificadas por comandos del pvmd
maestro usando mensajes. La operación de borrado es muy simple cuando se recibe un
mensaje. La operación de adición es hecha más cuidadosamente, en tres fases internas
para garantizar una disponibilidad global de los nuevos hosts sincronizados. Recibiendo un
mensaje, cada esclavo conoce la identidad del nuevo pvmd, y el nuevo pvmd conoce la
identidad de los previamente existentes. Cuando varios hosts son adicionados a la vez, el
trabajo es hecho en paralelo y la tabla de hosts es actualizada al mismo tiempo, permitiendo
que la operación completa se realice en menos tiempo que para un solo host.
Los descriptores de host ( hostd ) pueden ser compartidos por varias tablas de hosts,
esto es, cada hostd tiene un contador de referencia, de cuantas tablas de hosts lo incluyen.
Cuando la configuración de la máquina cambia, la descripción para cada hosts ( excepto los
adicionados y borrados ) permanece igual.
Canales de comunicación entre procesos.
El DemonioPVM 39
2.6
Las tablas de hosts tienen múltiples usos:
describen la configuración de la máquina
describen las colas de paquetes almacenadas
0
describen los buffers de mensajes.
.Permiten a los pvmd manipular conjuntos de hosts, por ejemplo cuando escogemos
un host candidato en cual se produce una tarea, Ó actualiza la configuración de la
máquina virtual.
0
0
2.6.3 Tabla de tareas
Cada pvmd mantiene una lista de todas las tareas que maneja. Cada tarea sin tomar
en cuenta su estado, es un miembro de la lista, ordenada por ctid ( ID de tareas). Muchas
tareas son también guardadas en una segunda lista, ordenada por fpid. La cabecera de
ambas listas es un descriptor de tareas ficticio, apuntado al localizador de tareas global (
locltasks ). Puesto que el pvmd frecuentemente necesita buscar una tarea por TID o PID,
deberá ser más eficiente para mantener estas dos listas como arboles balanceados.
2.6.4 Contexto de espera
Los contextos de espera ( waitcs ) son usados por el pvmd para almacenar
información del estado cuando un hilo de la operación 22 debe ser interrumpido. El pvmd no
es realmente multi - hilado, pero puede realizar operaciones concurrentemente. Por ejemplo,
cuando un pvmd tiene una llamada del sistema (desde una tarea) , algunas veces interactúa
con otro pvmd. Puesto que éste sirve como un ruteador de mensajes, no puede bloquearse
mientras espera que el pvmd remoto le responda. Cuando la contestación llega, el pvmd usa
la información guardada en el waifcs para completar la llamada del sistema y contestar ( a la
tarea ) . Los waifcs son numerados serialmente, y el número es enviado en la cabecera del
mensaje con la solicitud y regresa con la contestación.
El waitc incluye unos campos extra para manejar la mayoría de los casos restantes, y
un apuntador, wa-spec el cual puede apuntar a un bloque de datos extra en casos
especiales.
Algunas operaciones necesitan más de una fase de espera, esto puede ser en serie o
en paralelo, o aún anidadas ( si el pvmd remoto tiene que hacer otra solicitud ). En el caso
paralelo un waitc es creado para cada hosts remoto. Todas las operaciones paralelas que
existen en múltiples hosts son conjunciones: un grupo de waitcs semejantes termina,
esperando que cada waitc en el grupo haya terminado. Finalmente, cuando el waitc que
termina es sólo uno en su grupo, la operación está completa.
** Es la unidad básica de ejecucibn así como el objeto de trabajo más pequeño, y se ejecuta en el contexto de una tarea, comparte los
recursos de la tarea con otros hilos.
40
Parallel Virtual Machine
2.6.5 Detección y recuperación de fallas
Desde el punto de vista de pvmd, la tolerancia a fallas significa que éste puede
detectar cuando un pvmd remoto es dado de baja y recuperarse sin que se caiga el sistema.
Si el pvmd con falla es el maestro, se tiene que bajar el sistema. Desde el punto de vista de
las tareas, la detección de fallas significa que cualquier operación que involucra una baja de
hosts regresa una condición de error, en vez de suspenderse.
La detección de fallas se origina en el protocolo pvmd-pvmd, cuando un paquete esta
sin reconocerse durante tres minutos. La función hostfailentry() es llamada, está revisa
waitlist y termina cualquier espera involucrada con la falla del host.
2.7 La Biblioteca de programación.
La biblioteca libpvm es una colección de funciones que permiten a las tareas tener
interfaz con el pvmd y con las demás tareas. Contiene funciones para empaquetar y
desempaquetar mensajes. Una vez que ejecuta llamadas al sistema, PVM usa las
funciones de mensajes para enviar solicitudes de servicio al pvmd y recibir respuestas.
La biblioteca de programación está escrita en C y de ahí que naturalmente soporta
aplicaciones C y C++, e incluye muchas funciones para la programación de la interfaz.
En la primer llamada a cualquier función libpvm, se llama a pvmbeatasko para
inicializar el estado de la biblioteca y conectar la tarea a su pvmd. Los detalles de conexión
son ligeramente distintos entre tareas anónimas (no creadas por pvmd) y tareas creadas por
spawn. El pvmd publica las direcciones del socket, donde serán escuchadas para su
conexión, en /tmp/pvmd.<uid>. Este archivo contiene una línea tal como “7f000001:06f7” y
es como un atajo; las tareas creadas, heredan la variable de ambiente PVMSOCK, la cuál
contiene la misma información.
Una tarea creada con spawn necesita un segundo bit de datos para reconectarse
satisfactoriamente, es decir, su ID de proceso. Cuando una tarea es producida por el pvmd,
un descriptor de tarea es creado durante la fase exec. El descriptor es necesario, por
ejemplo, para esconder cualquier mensaje que llegue para la tarea antes de que sea
completamente reconectada y esté lista para recibirlo. Durante la reconexión, la tarea
identifica al pvmd por medio de su PID. Si la tarea es siempre el hijo de pvmd, entonces este
podría usar su PID como el regresado por getpid(), para identificarse. Para permitir que otros
procesos intervengan, tales como depuradores, el pvmd pasa una variable de ambiente,
PVMEPID a la tarea, la cual usa éste valor en vez de su PID real. La tarea también pasa su
PID real, de ahí que puede ser controlado por el pvmd por medio de señales.
pvmbeatask() crea un socket TCP y hace una conexión propia con el pvmd. Cada uno
debe proporcionar su identidad a el otro, para prevenir que un usuario distinto entre al
sistema. El pvmd y las tareas crean un archivo en su propio /tmp y éste es modificado sólo
por sus UID. Intentan escribir cada uno en los archivos del otro para verificar sus propios
archivos y cambiarlos. Si esto tiene éxito, han probado sus identidades. Note que esta
autenticación es solamente tan fuerte como el filesystem y la autoridad del root en cada
máquina.
.
La Biblioteca de Programación 41
2.7
Un número serial de protocolo, es comparado cuando se quiera que una tarea se
conecte a su pvmd o a otra tarea. Éste número debe ser incrementado cuando un cambio en
el protocolo haga a éste incompatible con la versión anterior.
Desconectar es mucho más sencillo. Esto puede ser hecho por un close desde
cualquier terminal, por ejemplo por la salida del proceso tarea. La función pvm-exit() ejecuta
un shutdown limpio, tal que el proceso puede ser conectado de poco tiempo después
(obtendría un diferente TID ).
2.8 Comunicación.
La comunicación PVM es en base a los protocolos Internet TCP y UDP. Los
manejadores de protocolo PVM se ejecutan como procesos normales ( pvmds y tareas ), sin
modificar el sistema operativo. Naturalmente, el desempeño de paso de mensajes realizado
es degradado por esta estrategia. El desempeño debería de ser mejor si el código fuera
integrado en el kernel, o alternativamente, la interfaz de red fuera disponible a los procesos,
pasando por alto el kernel. Sin embargo, cuando se ejecuta en Ethernet, los efectos de su
gasto general son mínimos. El desempeño es determinado por la calidad del código de red
en el kernel. Cuando se ejecuta en redes más rápidas Ó ruteos directos tarea-tarea mejora el
desempeño, minimizando el número de saltos entre uno y otro.
Esta sección describe cómo y dónde TCP y UDP son empleados, y describe los
protocolos PVM construidos sobre ellos.
Hay tres casos a considerar:
0
0
0
comunicación pvmd -pvmd
comunicación tarea pvmd
comunicación pvmd -tarea
-
2.8.1 Comunicación Pvmd-Pvmd
Los demonios PVM se comunican con los otros a través de sockets UDP. Un UDP es
un servicio de envío no confiable, el cual puede perder, duplicar o reordenar paquetes,
nosotros necesitamos un mecanismo con reconocimiento y recuperación. UDP impone un
límite en la longitud de un paquete, el cual solicita a PVM fragmentar mensajes grandes.
Usando UDP nosotros construimos un servicio confiable que envía paquetes con una
secuencia, y recibe una capa de mensajes, proporcionando una conexión similar a los pasos
TCP, pero con registros limitados.
Si se utiliza TCP, hay tres factores que lo hacen inapropiado:
0
Primero, la máquina virtual debe ser capaz de escalar a cientos de hosts. Cada conexión
TCP abierta consume un descriptor de archivo en el pvmd, y algunos sistemas operativos
!imitan el número de archivos abiertos a 32. Un sólo socket UDP puede enviar y recibir,
cualquier número de sockets remotos UDP.
42
Parallel Virtual Machine
Una máquina virtual compuesta de N hosts necesita hasta N(N-1)/2 conexiones, las
cuales son caras de establecer. Puesto que la identidad de cada host en una máquina
virtual es conocida, nuestro protocolo puede ser inicializado desde un estado correcto sin
una fase de conexión.
Finalmente, el servicio de paquetes pvmd-pvmd debe ser capaz de detectar cuando los
pvmds remotos ó los hosts se han perdido ó la red se ha caído. Para realizar esto,
necesitamos colocar temporizadores fuera de la capa del protocolo colocado.
Todos los parámetros y valores de default para la comunicación pvmd-pvmd son
definidos en el archivo ddpr0.h. También están especificados los código de mensaje para los
puntos de entrada (DM-XXX). Un número serial (DDPROTOCOL) es verificado siempre que
un pvmd es adicionado a la máquina virtual. Éste debe ser incrementado si se realiza un
cambio en el protocolo que lo hace incompatible con las versiones anteriores.
Las cabeceras para paquetes y mensajes son mostrados en las figuras 2.3 y 2.4.
Valores de múltiple-byte son enviados en "el órden de bytes de la red", esto es, primero el
byte más significativo.
Byte O
1
2
.................................................
..........................................................
.
...................................
<
3
.............................................................................................
TID fuente
.....................................................................................................................................
!
Número de secuencia
Número Ack (de reconocimiento) j
......................................................................................................................................
Sin uso
......................................................................................................................................
FIGURA 2.3 Cabeceras de paquetes pvmd- pvmd
Los campos fuente y destino almacenan los TlDs de la fuente y el destino final del
paquete, sin importar la ruta que éste toma.
.....................................................................................................................................
Código del mensaje
.....................................................................................................................................
i Codificación del mensaje o Número de Contexto de Espera Remoto j
.....................................................................................................................................
FIGURA 2.4 Cabecera del mensaje
2.8
Comunicación 43
Los números de secuencia y reconocimiento inician en 1 e incrementan su valor
hasta 65535. Éstos son inicializados en la tabla de hosts para nuevos hosts, de ahí que la
conexión no necesita ser establecida explícitamente entre pvmds.
Las bits de bandera son definidos como sigue:
SOM,EOM. Marcan el primer y último fragmento de un mensaje. Los fragmentos que
intervienen tienen limpios ambos bits. Éstos son usados por las tareas y por el pvmd para
detectar límites de mensaje.
DAT. Significa que los datos están contenidos en el paquete y el número de secuencia
válido. Si el paquete es de longitud cero, también debe ser enviado.
ACK . Significa que el campo de número de reconocimiento es válido. Este bit puede
ser combinado con el bit DAT para llevar reconocimiento de un paquete de datos. Sin
embargo actualmente el pvmd genera un paquete de reconocimiento para cada paquete de
datos, tan pronto como ,éste es recibido, a fin de obtener más seguridad en los datos.
FIN. Señala que el pvmd ha terminado la conexión. Un paquete con el bit FIN
colocado ( y DAT limpio ) señala la primera fase de un showdown ordenado. Cuando un
reconocimiento llega, un paquete final es enviado con ambos bits FIN y ACK colocados.
El estado de una conexión entre pvmds es guardado en una entrada en la tabla de
hosts ( estructura hostd ). El estado de una paquete es guardado en su estructura pkt. Los
paquetes en espera de ser enviados a un hosts son encolados en hd'tdx en forma FIFO.
Los paquetes pueden ser creados en tareas locales Ó por el pvmd y son anexados a la cola
por el código de ruteo. No son usadas colas de recepción porque los paquetes que entran
son pasados inmediatamente a través de otras colas de envío ó son reensamblados en
mensajes (o descartados). Cuando el mensaje es reensamblado completamente, el pvmd
pasa éste a la función netentry(), la cual lo despacha a su punto de entrada apropiado.
Para probar el desempeño sobre redes de alta latencia, el protocolo permite la salida
de múltiples paquetes en una conexión, así que dos o más colas son requeridas. hd-opq
almacena listas de paquetes sin reconocimiento, hd-rxq almacena paquetes recibidos fuera
de la secuencia, hasta que éstos puedan ser aceptados.
Cuando el paquete arriba al pvmd destino, cada uno genera un paquete de
reconocimiento y lo envía de regreso al transmisor. La diferencia de tiempo entre enviar un
paquete y recibir un reconocimiento, es usado para estimar el tiempo de ida y vuelta al host
remoto. Cuando el reconocimiento de un paquete llega, es removido del hd-opq y
descartado. Cada paquete sin reconocimiento tiene un temporizador que lleva la cuenta, y es
reenviado hasta que es admitido por el pvmd remoto.
Si un paquete se extingue debido a que se terminó su tiempo, se asume que el host
remoto Ó el pvmd fueron dados de baja o están incomunicados, y el pvmd ya no intenta
enviar paquetes, llamando a la función hostfailentry().
44
Parallel Virtual Machine
-
2.8.2 Comunicación tarea Pvmd
Una tarea se comunica con su pvmd sobre una conexión TCP. UDP debe parecer
más apropiado, porque ya es un servicio de envío de paquetes, mientras que TCP es un
protocolo fluido. Desafortunadamente UDP no es confiable, ya que puede perder paquetes.
Puesto que un sistema desconfiable requiere un mecanismo de recuperación (con
temporizadores) en ambas terminales y porque una suposición en el diseño es que la tarea
no pueden ser interrumpidas mientras se realizan ES,solo se puede usar TCP.
Nota: originalmente hemos usado datagramas de dominio UNIX, para la conexión pvmd tarea. Mientras esto parece ser confiable, depende de la implantación del sistema operativo,
éste protocolo no es ampliamente disponible como TCP.
-
2.8.3 Comunicación pwmd tarea
El sistema de envío de paquetes entre pvmd y tareas es mucho más sencillo que
entre dos pvmds porque TCP ofrece un envío confiable. El pvmd y la tarea mantienen una
cola FIFO de paquetes destinados para cada uno e intercambia lectura y escritura en la
conexión TCP.
La principal desventaja cuando usamos TCP para una pareja pvmd-tarea es que el
número de llamadas al sistema necesarias para transferir paquetes entre una tarea y pvmd,
aumenta. Sobre UDP, sólo sendto() y recvfrom() serán necesarios para transferir un paquete.
Puesto que TCP no proporciona marcas de registro (para distinguir paquetes de uno u otro),
tenemos que enviar el total de paquetes en la cabecera. Así un paquete también puede ser
enviado por una sola llamada write(), pero debe ser recibido por dos llamadas read(), la
primera para obtener la cabecera y la segunda para obtener los datos. Sin embargo, cuando
hay mucho de tráfico en la conexión pvmd tarea, una optimización sencilla puede reducir el
número promedio de llamadas de lectura hasta una por paquete. Así, cuando leemos el
cuerpo del paquete, el número de lecturas solicitadas aumenta por el tamaño de la cabecera
del paquete, éste puede tener éxito para obtener ambos, el cuerpo del paquete actual y la
cabecera del paquete siguiente, al mismo tiempo.
-
La cabecera del paquete es mostrada en la figura 2.5. Los números de secuencia no
son necesarios, y las únicas banderas son SOM y €OM, las cuales son usadas en el
protocolo pvmd-pvmd.
Comunicación 45
2.8
1
Byte O
2
3
.....................................................................................................................................
TID destino
...................................................................
..............................................................
TID fuente
....................................................................................................................................
Longitud del paquete
........................................................................................................
...........................
i
i
i
E : S i
0 ; 0 :
M !
M!
.
Sin uso
FIGURA 2.5
2.8.4 Mensajes en el Pvmd
Las funciones pkinf() y pksfr() anexan enteros y cadenas terminadas en NULL,
respectivamente, dentro de un mensaje. Las correspondientes funciones de
desempaquetamiento son upkint() y upksfr(). Los enteros sin signo son empaquetados con
su mismo signo, pero son desempaquetados usando upkuinf(). Otra función, upksfra//oc(),
asigna espacio dinámicamente para la cadena que ésta desempaca. Todas éstas funciones
usan funciones de bajo nivel byfepk() y byfeupk() para leer y escribir bytes para y desde
mensajes.
Los mensajes son enviados llamando a la función sendmessage(), la cual enruta el
mensaje a su dirección destino. Para un destino remoto, los fragmentos de mensajes son
unidos para empaquetarlos y enviarlos por la capa de ruteo de paquetes. Si un pvmd
programa una solicitud y se escoge así mismo como destino, no tiene que tratar el mensaje
en forma distinta, si no que envía el mensaje como es usual y espera una contestación, la
cual viene inmediatamente.
Los mensajes nuevos son reensamblados desde los paquetes por /oc/inpkf() si es
desde una tarea, Ó por nefinpkf() si son de otro pvmú.Una vez reensamblados, el punto de
entrada apropiado es llamado ( /oc/enfry(),netenfry(), o schedenfry() ).
Las únicas funciones que los pvmús hacen automáticamente son pings a otros pvmús
para verificar la red y borrar hosts dados de baja de la configuración de la máquina.
2.8.5 Codificadores de mensajes
Libpvm proporciona un conjunto de funciones para empaquetar cada tipo de datos
dentro del mensaje y recobrar éstos en otra terminal. Cualquier tipo de datos puede ser
empaquetado dentro del mensaje en uno de varios formatos de codificación. Cada buffer de
mensajes almacena un vector de funciones para codificar/decodificar todos los tipos, el cual
es inicializado cuando el buffer es creado.
46 Parallel Virtual Machine
Actualmente hay cinco conjuntos de codificadores (y decodificadores) definidos. El
vector codificador/decodificador
usado en un buffer es determinado por el formato del
parámetro pasado en pvm-mkbuf() cuando se crea un nuevo mensaje, y por el campo de
codificación de la cabecera del mensaje cuando el mensaje es recibido. Los dos más
comúnmente usados, empaquetan datos dentro de “raw“ (host nativo) y formatos “default”
(XDR). En “Inplace”, el dato es dejado en el lugar hasta que el mensaje es enviado. La
codificación “foo” puede empaquetar sólo enteros y cadenas de caracteres, y debe ser usado
cuando se compone un mensaje para el pvmd. Finalmente los decodificadores “alien” serán
instalados cuando un mensaje recibido no puede ser desempaquetado porque su formato no
es igual a cualquiera de los decodificadores disponibles en la tarea. Esto permite al mensaje
ser almacenado o enviado, pero cualquier intento por leer un dato de éste resultará en un
error.
2.8.6 Funciones que manejan empaquetamiento
Cuatro funciones manejan todo el tráfico de paquetes en la entrada y salida de libpvm.
Mroute() es llamada por funciones de alto nivel, tales como pvm-send() y pvm-recv()
para enviar y recibir mensajes. Éste establece cualquier ruta necesaria antes de llamar a
mxfer().
Mxfer() bloquea la transmisión hasta que un mensaje es recibido o hasta que termine
su temporizador. Ésta llama a mxinpuf() para copiar fragmentos de mensajes dentro de la
tarea y los reensambla en un mensaje.
Pvmmctl() es llamado por mxinput() siempre que un mensaje de control es recibido.
2.8.7 Mensajes de Control
Los mensajes de control son enviados como mensajes regulares a una tarea. Cuando
la tarea lleva un mensaje de control, en vez de encolarlo, lo pasa a la función pvmmcfl() y
entonces lo descarta. Como loc/enfry() en el pvmd, pvmmcfl() es un punto de entrada en la
tarea, causando que tome una acción. La principal diferencia es que los mensajes de control
no pueden siempre ser usados para obtener la atención de las tareas, puesto que ésta debe
de estar en mxfer(), enviando ó recibiendo los mensajes.
Las siguientes etiquetas de mensajes de control son definidas. Las primeras tres son
usadas por los mecanismos de ruteo directo. Los futuros mensajes de control pueden ser
usados para hacer cosas tales como colocar depuradores y máscaras de seguimiento en la
tarea conforme ésta se ejecute.
Etiqueta
TC-CONREQ
TC-CO NACK
TC-TASKEXIT
TC-NOOP
TC-OUTPUT
Significado
Solicitud de conexión
Reconocimiento de conexión
Tareas que salieron/no han salido
No hacer nada
Reclama salida estándar de los hijos
Comunicación 47
2.8
2.8.8 Ruteo directo de mensajes
El ruteo directo permite a una tarea enviar mensajes a cualquier otra tarea a través de
una liga TCP, evitando que la cabecera se copie en ellos a través de los pvmds. Este
mecanismo es implementado enteramente en libpvm, tomando ventaja de las facilidades de
la notificación y de mensajes de control.
Por default, cualquier mensaje enviado a otra tarea es ruteado al pvmd, el cual lo
encamina a su destino. Si el ruteo directo está disponible (pvmrouteopt=PvmRouteDirect)
cuando un mensaje (direccionado a una tarea) es pasado a mroute(), intenta crear una ruta
directa, si no existe ya. La ruta puede ser no valida ( si el destino no existe) o bien,
concedida Ó refutada por la tarea destino.
2.8.9 Envío Múltiple
Libpvm proporciona la función pvm-mcast(), para enviar un mensaje a varios destinos
simultáneamente, esperando que utilice menos tiempo que con varias llamadas pvm-send().
La implantación actual permite rutear varios mensajes a través de pvmd y utiliza una salida
de l : N para simplificar la propagación de la tolerancia a fallas. El problema es estar seguro
que la falla de un host no causará la pérdida de cualquier mensaje. La capa de ruteo de
paquetes de la pvmd coopera con iibpvm para enviar en forma simultánea un mensaje.
2.9 Consideraciones generales de desempeño.
No hay limitaciones para los estilos de programación en PVM de los que un usuario
puede hechar mano para resolver una problemática. Cualquier control específico y estructura
puede ser implementada en PVM mediante el uso apropiado de las construcciones. Por otro
lado hay ciertas consideraciones que el programador de aplicaciones debe tener presente
cuando programe cualquier sistema de paso de mensajes.
La primer consideración, es la granularidad de las tareas. Esto es típicamente medido
como la razón entre el número de bytes recibidos por un proceso y el número de
operaciones de punto flotante realizadas en el mismo. Haciendo algunos cálculos sencillos
respecto a la velocidad computacional de las máquinas en la configuración PVM y el ancho
de banda en la red, un usuario puede obtener una aproximación de la granularidad de la
tarea que trata de utilizar. Una mayor granularidad y la más alta velocidad implican
frecuentemente un aumento en el paralelismo disponible.
La segunda consideración, es el número de mensajes enviados. El total de bytes
recibidos puede ser enviado en muchos mensajes pequeños o en pocos mensajes grandes.
Mientras usar pocos mensajes grandes reduce el tiempo de reconstrucción del mensaje,
esto no siempre causa la disminución del tiempo total de ejecución. Hay casos donde
pequeños mensajes pueden traslaparse con otra operación, de ahí que su cabecera es
marcada. La capacidad para traslapar mensajes con cálculos y el número Óptimo de
mensajes por enviar depende de la aplicación.
48
Parallel Virtual Machine
Una tercer consideración es, si se debe convertir la aplicación a paralelismo funcional
ó a paralelismo de datos. Definimos el paralelismo funcional como máquinas diferentes
desarrollando tareas diferentes en una configuración PVM. Por ejemplo una
supercomputadora de vector puede resolver una parte de un problema adaptado para
vectorización, un multiprocesador puede resolver otra parte del problema adaptado a
paralelización y una estación de trabajo gráfica puede visualizar los datos generados, en
tiempo real. Cada máquina desarrolla diferentes funciones ( posiblemente sobre los mismos
datos ).
En el modelo de paralelismo de datos, el dato es particionado y distribuido entre
todas las máquinas en la configuración PVM. Las operaciones ( frecuentemente similares )
son realizadas en cada conjunto de datos y la información es pasada entre los procesos
hasta que el problema esté resuelto. El paralelismo de datos ha sido común en
multiprocesadores de memoria distribuida, porque éste requiere sólo escribir un programa
paralelo que es ejecutado en todas las máquinas y porque muchas veces puede ser
escalable a un centenar de procesadores.
2.1 O Consideraciones particulares de la red.
Hay consideraciones adicionales para quien desarrolla aplicaciones en paralelo y
desea ejecutarlas sobre una red de máquinas. Su programa paralelo estará compartiendo la
red con otros usuarios. Estos ambientes multiusuarios y/o multitareas afectan la
comunicación y el desempeño computacional de sus programas en forma compleja.
Primero considere los efectos de tener distinto poder computacional en cada máquina
de la configuración. Esto puede ser debido a que se tiene una colección heterogénea de
máquinas en la máquina virtual, las cuales difieren en su velocidad computacional. Sólo,
entre diferentes marcas de estaciones de trabajo se puede tener dos ordenes de magnitud y
poder computacional distintos. En supercomputadoras puede ser más grande la diferencia.
Pero, aún si el usuario especifica una colección de máquinas homogéneas, él puede ver
grandes diferencias en el desempeño de cada máquina. Esto es causado por sus propias
multitareas Ó por las tareas de otros usuarios en un subconjunto de las máquinas que se
encuentran en la configuración. Si el usuario divide su problema en partes idénticas una para
cada máquina entonces, la consideración de arriba puede afectar adversamente su
desempeño. Su aplicación se ejecutará tan lenta como la tarea en la máquina más lenta. Si
las tareas se coordinan con cada una de las otras, entonces aún las máquinas más rápidas
se alentarán por esperar los datos de las tareas lentas.
La segunda consideración son los efectos de retardo de mensajes grandes a través
de la red. Esto puede ser causado por la distancia entre las máquinas si se está empleando
una red de área amplia ó por la congestión causada por los programas propios ó por los de
otros usuarios de la red. Considerando que las redes Ethernet tienen un bus. Así que sólo un
mensaje puede estar en el bus en cualquier momento.
Si la aplicación está diseñada para que cada una de estás tareas sólo envié mensajes
a su tarea vecina, se podría asumir que no hay congestión. En un multiprocesador de
memoria distribuida, como en el Intel Paragon, no habría congestión y todos los envíos
Consideraciones particulares de la red 49
2.10
deberían procesarse en paralelo. Pero sobre una Ethernet los envíos son conducidos
serialmente, variando el tiempo de retardo (latencias) en los mensajes que llegan de tareas
vecinas. Otras redes tales como Token Ring, FDDl y HiPPI, todas tienen propiedades que
pueden causar variación en la latencia. El usuario debe determinar si la tolerancia de la
latencia debe ser diseñada dentro de su algoritmo.
La tercer consideración es que el desempeño y la efectividad en el ancho de banda de
la red, cambia dinámicamente conforme más usuarios comparten sus recursos. Una
aplicación puede obtener muy buena velocidad durante su ejecución y una pobre velocidad
con la misma ejecución pocos minutos después. Durante la ejecución de una aplicación ésta
puede tener un patrón de sincronización fuera de lo normal, causando que algunas tareas
estén esperando datos. En el peor caso, un error de sincronización puede existir cuando en
una aplicación la carga dinámica de la máquina fluctúa en una forma particular. Como tales
condiciones no se pueden reproducir fácilmente, éstos errores son muy difíciles de
encontrar.
2.11 Balance de Carga
En un ambiente de redes multiusuario, hemos encontrado que el balance de carga
puede ser el factor de desempeño más importante. Hay muchos esquemas de balance de
carga para programas paralelos. En está sección mostraremos los tres más comunes e
importantes esquemas utilizados en redes de computadoras.
El método más sencillo, es el de balance de carga estático. En este método el
problema es dividido y las tareas son asignadas a los procesadores sólo una vez. La
partición de datos puede ocurrir fuera de línea, antes que el trabajo sea comenzado Ó puede
ocurrir como un paso anterior a la aplicación. El tamaño de las tareas Ó el número de tareas
asignadas a una máquina, puede variar a causa del distinto poder de cálculo en las
máquinas. Puesto que todas las tareas pueden ser activadas desde el inicio, éstas pueden
comunicarse y coordinarse con cualquier otra. En una red ligeramente cargada, el balance
de carga estático puede ser bastante efectivo.
Cuando el balance de carga está variando, el esquema de balance de carga dinámico
es requerido. El método más común es llamado el paradigma de Pool of Task ( Banco de
tareas ). Éste es típicamente implementado en un programa maestro/esclavo, donde el
programa maestro crea y almacena el pool y cede tareas a los programas esclavo conforme
van terminando. El pool es usualmente implementado como una cola, y si las tareas varían
en tamaños entonces las tareas más grandes son colocadas cerca de la cabeza de la cola.
Con este método todos los procesos esclavos están ocupados como tantas tareas
haya en el pool. Un ejemplo del paradigma del pool de tareas puede ser visto en el programa
xep proporcionado con el código fuente bajo pvm3/xep.
Puesto que las tareas se inician y se detienen arbitrariamente con este método, éstas
son mejor adaptadas a aplicaciones que no requieren comunicación entre programas
esclavo, y sólo se comunican con el maestro y con los archivos.
Un tercer esquema de balance de carga, no usa un proceso maestro, requiere que en
un tiempo determinado todos los procesos reexaminen y redistribuyan su carga de trabajo.
50 Parallel Virtual Machine
Algunas implantaciones nunca se sincronizan con todos los procesos, pero en cambio
distribuyen su exceso de carga solamente con sus vecinos. Otras esperan hasta que un
proceso señale que su balance de carga ha obtenido alguna tolerancia mayor antes de ir a
una redistribución de carga y esperar un tiempo fijo.
CAPíTULO 3
Aplicaciones bajo (PVW
3.1 Ejemplos de programación de PVM en lenguaje C .
Esta sección contiene dos programas de ejemplo, cada uno ilustra una forma distinta
de organizar aplicaciones en P V M 3. Estos ejemplos y otros son proporcionados con el
código fuente en PVM-ROOT/examples.
El primer ejemplo es un modelo maestro/esclavo con comunicación entre esclavos. El
segundo es un programa con el modelo un programa - múltiples datos (Single Program
Multipie Data, SPMD).
En el modelo maestro/esclavo, el programa produce y direcciona un número
específico de programas esclavo, los cuáles realizarán las operaciones. P V M no está
restringido a éste modelo. Por ejemplo, cualquier tarea P V M puede inicializar procesos en
otras máquinas. Pero un modelo maestro/esclavo es un paradigma de programación útil y
sencillo de ilustrar. El maestro llama a pvm-myfid(), la cuál, como es la primer llamada PVM,
registra ésta tarea en el sistema PVM. Éste entonces, llama a pvm-spawn() para ejecutar un
número dado de programas esclavo en otras máquinas. El programa maestro contiene un
ejemplo de envío de mensajes en PVM. El maestro envía a los esclavos el número de
esclavos comenzados y una lista de todos los tids de los esclavos. Cada esclavo llama a
pvm-mytid() para determinar su ID de tarea en la máquina virtual, luego usa el dato para
enviarlo a el maestro, para crear un orden único de O a nproc-I .
Subsecuentemente, pvm-send() y pvm-recv() son usados para pasar mensajes entre
procesos.
Cuando terminan, todos los programas P V M llaman a pvm-exif() para desconectar
cualquier sócket de los procesos, restablecer buffers de I10 y guarda el estado de los
procesos que se están ejecutando.
En modelo SPMD, sólo hay un programa y no existe un programa maestro
directamente. Tales programas son algunas veces llamados hostless. En el segundo
ejemplo, el usuario comienza la primer copia del programa, verificando pvm-parent(), ésta
51
52
Aplicaciones bajo PVM
copia puede determinar que ha sido creada por PVM y que debe ser la primer copia. Ésta
después produce múltiples copias de sí misma y las pasa el arreglo de ti&. Hasta este
punto, cada una de las copias es igual y puede trabajar su partición de datos en colaboración
con los otros procesos. Usando pvm-parent() evita comenzar el programa SPMD desde la
consola pvm, porque pvm-parent() regresará el fid de la consola. Este tipo de programas
SPMD, debe ser comenzado desde el prompt de UNIX.
VERSIÓN MAESTRO/ESCLAVO
PROCESO MAESTRO
# include pvm3.h”
#define SLAVENAME “slavel”
void main(void)(
int mytid;
int tids[32];
int n;
int nproc;
int i;
int who;
int msgtype;
float datos[ 1001;
resuIt[32];
mytid=pvm-mytido;
/* identificador de tareas */
/* identificador de esclavos */
/* número de datos */
/* número de esclavos */
/* contador para el ciclo for */
/* de que esclavo se trata */
/* etiqueta de mensajes */
/* arreglo de datos */
/* resultado de los esclavos */
/* asigna a mytid el TID de el proceso en
la máquina virtual, este TID es único */
puts(“ ¿Cuantos programas esclavos quieres (1-32): ?
scanf(“%d”,&nproc);
pvm-s pawn(SLAVENAM E,(char**)O, O,”” ,nproc,tids); /* comienza nproc cop¡as de
‘I);
un archivo ejecutable en la máquina virtual, SLAVENAME tiene el
nombre de este archivo ejecutable; este archivo debe de estar
colocado en $HOME/pvm3/bin/SGI ya que es ahí donde PVM
busca por default los archivos ejecutables, si no se quiere dejar
ahí entonces se debe de indicar cual es el nuevo path para este
host en el archivo rhosts, (char**)O es un puntero a un arreglo de
argumentos de tareas, la bandera O es debido a que cualquier
host de la máquina virtual puede iniciar la tarea por lo que PVM
escoge el host mas apropiado, las en ellas se pondría el host si
la bandera anterior fuera distinta de cero, nproc es el número de
esclavos que escogió el usuario y finalmente tids es el arreglo de
tids para que PVM lo llene con los tids de los esclavos creados */
‘I”
n= 100;
inicializa-datos();
/* número de datos a procesar */
/* inicializa datos */
3.1
Ejemplos de programación de PVM en lenguaje C 53
/* transmite datos iniciales para tareas esclavos */
/* limpia el buffer y crea uno nuevo para empaquetar
pvm-initsend( PvmDataRaw);
un nuevo mensaje, como los mensajes sólo se enviaran entre la
misma máquina entonces se usa PvmDataRaw */
/* empaqueta el buffer de mensajes activo, en el
pvm-pkit(&nproc, 1 ,I ) ;
primer argumento se pone el arreglo a enviar, en este caso se
pondrá el número de procesos esclavos iniciados, el segundo
argumento es el número total de items a enviar y el tercer
argumento el número de pasos para empaquetar el item */
pvm-pkit(&n,l , I )
pvm-pkfloat(dato,n, 1);
pvm-mcast(fiús,nproc,O);
/* se empaqueta el número de datos */
/* se empaqueta el arreglo de datos de punto flotante
en un sólo paso */
/* Multi envía estos datos a el buffer activo de
mensajes con lo que cada esclavo recibe una copia
de los datos, fids es el arreglo de identificadores de
esclavos con etiqueta cero */
/* espera resultados de los esclavos */
mstype =5;
/* etiqueta del maestro */
for (¡=O;icnproc;i++){
/* recibe un mensaje, bloquea un proceso hasta que
un mensaje con etiqueta msgtype ha arribado esto
debido a la etiqueta -1 pvm-recv() esta aceptando un
mensaje desde cualquier proceso quien ha igualado
msgtype */
pvm-upkit(&who,l , I ) ;
/* desempaqueta el TID de el esclavo que envío sus
datos */
pvm-upkfloat(&result[who], 1,l);
/* desempaqueta los resultados de el
esclavo who */
printf(“ Conseguí %f desde % d\n “,result[who],who);
pvmexit ();
} /* fin for */
pvm-recv(- 1,msgtype);
}/* fin maestro */
54 Aplicaciones bajo PVM
PROCESO ESCLAVO
# include “ pvm3.h”
void main(void){
int mytid;
int tids[32];
int n;
int m;
int nproc;
int i;
int master;
int msgtype;
float datos[ 1001;
float result[32];
/* identificador de tareas */
/* identificador de esclavos */
/* número de datos */
/* que esclavo soy */
/* número de esclavos */
/* contador para el ciclo for */
/* identificador para el maestro */
/* etiqueta de mensajes */
/* arreglo de datos */
/* resultado de los esclavos */
mytid=pvm-m ytid() ;
/* registra el proceso en la máquina
virtual */
/* Recibe datos del programa maestro */
msgtype=O;
pvm-recv(-I ,msgtype);
pvm-upkint(&nproc,l ,1);
pvm-upkint(fids,nproc, 1);
pvm-upkint (&n,l , I ) ;
pvm-upfloat(dato,n, 1);
/* etiqueta */
/* recibe datos de cualquier proceso */
/* desempaqueta el número de esclavos */
/* desempaqueta el arreglo de tids */
/* desempaqueta el número de datos */
/* desempaqueta el arreglo de datos */
/* Determina que esclavo es */
for(¡=O;
i<nproc;i++){
if(myt id==ti&[ ¡I){
me=¡;
break;
1
/* trabaja con los datos */
result = works( me,n,datos,tids,nproc);
/* envía el resultado a el maestro */
pvm-initsend( PvmDataDefault);
pvm pkint(&me,l ,I);
pvmIpkfloat(&result,l ,I);
msgtype =5 ;
master = pvm-parent();
/* limpia el buffer y se prepara para empaquetar un
nuevo mensaje */
/* empaqueta que esclavo soy */
/* empaqueta el resultado */
/* retorna el TID de el proceso que produjo esta tarea
en este caso este proceso fue creado por el proceso
mastro */
Ejemplos de programación de PKM en lenguaje C 55
3.1
pvm-send(master,msgtype);
/* envía los datos en el buffer activo master es el
destino con etiqueta msgtype */
pvmexit();
} /* fin esclavo */
VERSION SPMD
#define NPROC 4
#include “pvm3.h”
void main(void)
{
int mytid;
int fids[NPROC];
int me;
int i;
/* Número de copias a generar *I
mytid = pvm-mytido;
fiús[O]=pvm-parent();
if (fiús[O] < O)
{
fids[O]=mytido;
me = O;
I* Se registra en PVM *I
/* Registra si es el padre o un hijo */
/* ldentificador de tareas *I
/* Arreglo de fiús*/
/* ldentificador de las tareas producidas */
/* Contador del ciclo for *I
I* Entonces yo soy el padre *I
I* Inicio copias de mi mismo *I
pvm-spawn(“sped”, (char **)O, O, “”,NPROC-l ,&fids[l]);
/* Envía el arreglo de fids*/
pvmjnitsend(PvmDataDefau1t);
/* a los hijos *I
pvm-p kint (fids,NPROC,I);
pvm-mcast(&fids[ 11, NPROC-1,O);
}
/* Entonces, soy un hijo *I
else{
/* Recibe el arreglo de fids*/
pvm-recv( fids[O],O);
pvm-upkint( fids,NPROC, 1);
for(i=l ; i<NPROC;i++)
if(mytid == tiail ) {me = i; break; }
/* Todas las NPROC tareas son iguales ahora
e
e
e
1
56 Aplicaciones bajo PVM
dowork(int me,int *fids,int nproc){
int token,dest,count=l ,stride=l ,msgtag=4;
if( me==O ){
token = fiús[O];
pvm-initsend( PvmDataDefault );
pvm-pkint ( &token,count,stride );
pvm-se nd ( ti&[ me+1] ,msgtag);
pvm-recv(fids[nproc- I],
msgtag);
else{
1
1
pvm-recv( fiús[me-I 1, msgtag);
pvm-upkint(&token,count,stride);
pvm-initsend( PvmDataDefault);
pvm-p kint(&token,count ,stride);
dest=(me==nproc-I)? fiús[O]:fiús[me+l];
pvm-send(dest,msgtag);
1
3.2 Escribiendo aplicaciones.
Los programas de aplicación vistos en PVM son ejemplos generales y flexibles de
programación paralela que soportan el modelo de paso de mensajes. Éstos programas
pueden ser ejecutados en tres diferentes niveles:
rn Modo transparente, en el cual las tareas son automáticamente ejecutadas en los
hosts más apropiados ( generalmente los menos cargados ).
Modo dependiente de la arquitectura, en el cual el usuario puede indicar las
arquitecturas especificas en las que determinadas tareas serán ejecutadas.
Modo de bajo nivel, en el cual un host particular puede ser especificado para
ejecutar determinadas tareas.
Tal estratificación permite flexibilidad, mientras conserva la habilidad para explotar el
potencial particular de cada máquina en la red.
Los programas de aplicación bajo PVM tienen control arbitrario y estructuras
dependientes. Es decir, en cualquier punto en la ejecución de una aplicación concurrente, los
procesos existentes pueden tener vínculos entre ellos, y también, cualquier proceso puede
comunicarse y/o sincronizarse con cualquier otro. Esto se permite para la forma más usual
de programación paralela, MlMD (Multiple Instruction Multiple Data, Múltiples instrucciones
múltiples datos), pero en la práctica muchas aplicaciones concurrentes son más
estructuradas. Dos estructuras típicas son el modelo SPDM, en el cual todos los procesos
son idénticos y el modelo de maestro/esclavo, en el cual un conjunto de procesos esclavos
realizan el trabajo para uno o más procesos maestros.
Interfaz de Usuario 57
3.3
3.3 lnterfaz de Usuario.
En esta sección daremos una breve descripción de las rutinas de la biblioteca de
usuario en PVM3.3. Esta sección esta organizada según el tipo de función de cada rutina.
¿Cómo el usuario debe tomar ventajas de esta funcionalidad, y de las rutinas C que
pertenecen a esta función?.
En PVM 3 todas las tareas son identificadas por un entero que es proporcionado por
el demonio local pvmd ( tid ).
Éste es similar al identificador de procesos ( PID ) en sistemas UNlX excepto que el
tiú tiene codificado en él la dirección del proceso en la máquina virtual. Esta codificación
permite a las rutinas una comunicación más eficiente, y permite una integración más eficaz
de los multiprocesadores.
Todas las rutinas PVM son escritas en C . Las rutinas en C++ pueden ser ligadas con
las bibliotecas PVM. Las rutinas en Fortran pueden llamar a estas funciones a través de una
interfaz Fortran 77 proporcionada con el código fuente de PVM3. Esta interfaz traslada
argumentos, los cuales son pasados por referencia en Fortran.
3.3.1 Control de Procesos
int tid = pvm-mytid(void)
La rutina pvm-mytid() registra esta tarea dentro de PVM, en su primer llamada y
genera un único fiú si el proceso no fue generado con pvm-spawn(). Este regresa el tiú de
esta tarea, y puede ser llamado múltiples veces. Cualquier llamada que se realice en PVM
(no sólo pvm-mytid ) registra una tarea en PVM , si la tarea no ha sido registrada antes con
otra llamada.
int info = pvm-exit(void)
La rutina pvm-exit() comunica al demonio local pvmd, que esta tarea abandona la
máquina virtual. Esta rutina no termina la tarea, la cuál puede continuar su ejecución como
cualquier otro proceso UNlX sin utilizar P VM.
Inf numt = pvm-spawn(char *task, char **argv, int flag, char *where,
int ntask, int "tids)
La rutina pvm-spawn() inicializa ntask copias de un archivo task ejecutable en la
máquina virtual, argv es un apuntador a un arreglo de argumentos para task con el fin del
arreglo inicializado con NULL. Si la tarea no tiene argumentos entonces argv es NULL. El
argumento flag es usado para especificar algunas opciones, las cuales son:
58 Aplicaciones bajo PVM
PvmTaskDefa ult
PvmTaskDefault
PvmTaskHost
PvmTaskArch
PVM escoge donde producir tareas
PVM escoge donde producir tareas
El argumento where especifica un host para producirse ahí.
El argumento where especifica una arquitectura PVM-ARCH
para producirse ahí.
Comienza estas tareas bajo un depurador
La llamada PVM en estas tareas genera una busqueda de
datos
Iniciará tareas bajo un nodo MPP front-endkervice
Comienza tareas bajo un conjunto de host complemento
PvmTaskDebug
PvmTaskTrace
PvmMppFront
PvmHostCompl
En numf es colocado el número de tareas producidas exitosamente o un código de
error si las tareas no pueden ser inicializadas. Si las tarea fueron iniciadas, entonces
pvm-spawn() regresa un vector de fids de tareas producidas y si algunas tareas no pueden
ser iniciadas, los códigos de error correspondientes, serán colocados en las Últimas ( nfask numf ) posiciones del vector.
Pvm-spawn() también puede iniciar tareas en multiprocesadores. En el caso de la
Intel-¡PSC/860, se aplican las siguientes restricciones: cada llamada spawn obtiene un
subcubo de tamaño nfask y carga el programa task en todos estos nodos. La iPSC1860 OS
tiene una asignación limite de 10 subcubos para todos los usuarios, de ahí que es mejor
comenzar un bloque de tareas en una iPSC/860 con sólo una llamada a pvm-spawn() en
vez de varias llamadas. Dos bloques diferentes de tareas producidas separadamente en la
iPSC/860, pueden comunicarse con cada una de las otras, más rápido que cualquier otra
tarea PVM, aún cuando están en subcubos separados.
int info
= pvm-kill(int fid)
La rutina pvm-kill termina las tareas PVM identificadas por fid. Esta rutina no está
diseñada para eliminar la tarea que está llamando ésta función, lo cual debería ser realizado
por la llamada pvm-exit( ) seguida por exif().
3.3.2 Información
int fid = pvmjarent(void)
La rutina pvm-parenf() regresa el fid del proceso que generó esta tarea Ó el valor de
PvmNoParenf si no fue creada por pvm-spawn().
int pstat
= pvmjsfat(.int fid)
La rutina pvm-psfaf regresa el estado de una tarea PVM identificada por fid. Esta
rutina regresa PvmOk si la tarea se está ejecutando, PvmNoTask si no, Ó PvmBadParent si
el fides inválido
int mstat = pvm-mstat(char *host)
Interfaz de Usuario 59
3.3
La rutina pvm-mstat regresa PvmOk si el host está activo. PvmHostíile si el host esta
incomunicado o PvmNoHosf si el host no está en la máquina virtual. Esta información puede
ser útil cuando se crean aplicaciones con tolerancia a fallas.
int info = pvm-conf¡g(int *nhost,int *narch,struct pvmhostinfo **hostp)
La rutina pvm-config regresa información acerca de la máquina virtual incluyendo el
número de host, nhost, y el número de formatos de datos distintos, narch. hostp es un
apuntador a un arreglo de estructuras pvmhostinfo. El arreglo es de tamaño nhost. Cada
estructura pvmhostinfo contiene el tid del demonio pvmd, el nombre del host, nombre de la
arquitectura y la velocidad relativa del CPU, en la configuración. PVM no usa o determina el
valor de la velocidad. El usuario puede colocar éste valor en el hosffile y recuperar este valor
con pvm-config(), para usarlo en una aplicación.
int info = pvm-tasks(int which, int *ntask,struct pvmtaskinfo ** taskp)
La rutina pvm-tasks regresa información acerca de las tareas PVM ejecutándose en la
máquina virtual. El entero which especifica cuáles tareas regresarán información. Las
opciones presentes son:
0
0
0
(O), lo que significa que todas las tareas regresarán información,
el tid de un pvmd, lo que significa que sólo las tareas ejecutándose en ese
host regresan información.
ó el tid de una tarea, lo que significa que sólo esa tarea devolvera información.
El número de tareas es regresado en ntask. taskp es un apuntador a un arreglo de
estructuras pvmtaskinfo. El arreglo es de tamaño ntask. Cada estructura taskinfo contiene el
tid, el fid del pvmd, el tid de la tarea padre, y el nombre del archivo producido. ( PVM no
conoce el nombre del archivo de tareas iniciadas manualmente).
int dtid = pvm-tidtohost(int tid)
Si un usuario necesita conocer en que host se está ejecutando una tarea especifica,
la función pvm-fiútohost() proporciona esa información.
3.3.3 Configuración Dinámica
int info = pvm-addhosts( char **hosts, int nhost, int *infos)
int info = pvm-delhosts( char **hosts, int nhost, int *infos)
Estas rutinas agregan o eliminan un conjunto de hosts en la máquina virtual. En info
es regresado el número de hosts agregados exitosamente. El argumento infos es un arreglo
de longitud nhost que contiene el código de estado para cada host individual, que ha sido
agregado o eliminado. Esto permite al usuario verificar si sólo uno de los hosts de un
60 Aplicaciones bajo PVM
conjunto causó algún problema y así evitamos agregar o eliminar el conjunto entero de
hosts otra vez.
3.3.4 Señalización
int info = pvm-sendsig( int tid, int signum)
pvm-sendsig() envía una señal signum a otra tarea PWM identificada por tid.
int info
= pvm-notify(nt what, int msgtag, int cnt, int *fids)
La rutina pvm-nofify() solicita a PVM que informe a la tarea que invocó a
pvm-notfly(), la ocurrencia de alguno de los siguientes eventos:
Pvm TaskExit
PvmHostDelete
PvmHostAdd
Notifica si una tarea sale
Notifica si un host es borrado (o ha fallado)
Notifica si un host es adicionado
En respuesta a la solicitud de notificación, algún número de mensajes son enviados
por PVM de regreso a la tarea solicitante. Los mensajes son etiquetados con el código (
msgtag ) proporcionado por pvm-notify.
Si el host donde una tarea A está siendo ejecutada falla, y una tarea B ha solicitado si
la tarea A ya terminó, entonces la tarea B será notificada de lo que sucedio aún cuando la
salida fuera causada indirectamente.
3.3.5 Opciones de colocación y obtención
int oldVal= pvm-setoptfint what, int val)
int val = pvm-getopt(int what)
Las rutinas pvm-setopt( ) y pvm-getopt( ) son funciones de propósito general, que
permiten al usuario colocar u obtener opciones del sistema PWM. En PWM 3, pvm-setopt()
puede ser usada para colocar varias opciones incluyendo: impresión automática de
mensajes de error, nivel de depuración y método de ruteo de comunicación para todas las
subsecuentes llamadas PWM. pvm-setopt() regresa el valor previo de colocación en oldval.
En PWM 3.3 el argumento what puede tomar los siguientes valores:
Opción
Significado
PvmRoute
PvmDebugMask
PvmAufoErr
PvmOutputTid
1
2
3
4
PvmOutputCode
Pvm TraceTid
5
6
Política de ruteo
Mascaras de depuración
Reporte automático de errores
Dispositivo de salida estándar para
tareas hijos
Salida de la etiqueta de mensaje (msgtag)
Señala el dispositivo para tareas hijos
Paso de Mensajes 61
3.4
PvmTraceCode
PvmFragSize
PvmResvTids
7
8
9
Señala etiqueta de mensaje ( msgfag )
Tamaño del fragmento de mensaje.
Permite a los mensajes ser enviados para
etiquetas y fids reservados.
Pvm-sefopf() puede colocar varias opciones de comunicación en PVM, tales como
método de ruteo ó tamaños de fragmento a usar. Esta rutina puede ser llamada múltiples
veces durante una aplicación, para colocar selectivamente ligas de comunicación directa de
tarea a tarea. Su uso típico es llamar a esta función después de pvm-myf¡d().
3.4 Paso de mensajes:
El envío de un mensaje está compuesto de tres pasos en PVM :
0
0
0
Primero, un buffer de envío debe de ser inicializado mediante una llamada a
pvm-initsend() o pvm-mkbufo.
Segundo, el mensaje debe ser " empaquetado dentro de este buffer usando
cualquier número y combinación de las rutinas pvm-pk*.
Tercero, el mensaje completo es enviado a otro proceso llamando a la rutina
pvm-send() ó llamadas múltiples con la rutina pvm-mcasf().
"
Además, hay funciones de comunicación colectiva que operan sobre un grupo entero
de tareas.
PVM también proporciona la rutina, pvm-psend(), la cual combina los tres pasos en
una sola llamada. Esto permite la posibilidad de hacer más rápidas las implantaciones
internas. pvm-psend() sólo empaqueta y envía un arreglo contiguo de un sólo tipo de datos.
pvm-psend() usa su propio buffer de envió, así que esto no afecta a un buffer que ha sido
empaquetado para usarse con pvm-sendo.
Un mensaje es recibido por llamadas a rutinas que reciben ya sea un bloque Ó un nobloque y entonces desempaqueta cada uno de los elementos empaquetados en el buffer de
recepción. Las rutinas de recepción pueden ser usadas para aceptar:
0 cualquier mensaje
cualquier mensaje desde una fuente específica
cualquier mensaje con una etiqueta específica
mensajes con etiqueta y fuente dadas.
Hay también una función de prueba que verifica si un mensaje ha llegado, pero aún
no se ha desempacado.
62 Aplicaciones bajo P V M
3.4.1 Buffers de mensajes
Las siguientes rutinas para manejo de buffers de mensajes, son requeridas sólo si el
usuario desea manejar múltiples buffers de mensajes dentro de una aplicación. No se
requieren múltiples buffers para la mayoría de los pasos de mensajes entre tareas. En PVM
3 hay un buffer de envío y uno de recepción activos por proceso en cualquier momento. El
desarrollador de aplicaciones puede crear cualquier número de buffers de mensajes e
intercambiarlos entre ellos para empaquetar y enviar datos. Las rutinas de
empaquetamiento, desempaquetamiento, envío y recepción sólo afectan a los buffers
activos.
Inf bufid
= pvm-mkbuf( inf encoding)
La rutina pvm-rnkbuf crea un nuevo buffer de envío vacío y especifica el método de
codificación usado para empaquetar mensajes. Este regresa el identificador de buffer, bufid.
Las opciones de codificación son:
PvmDafaDefaulf. La codificación XDR es usada por default, porque PVM no sabe si el
usuario va a agregar otra máquina heterogénea antes que se envíe este mensaje. Si el
usuario sabe que el siguiente mensaje será enviado a una máquina que entiende el formato
original entonces, él puede usar la codificación PvmDafaRaw y ahorrar costos de
codificación.
PvmDafaRaw. La codificación no se hace, los mensajes son enviados en su formato
original. Si el proceso de recibimiento no puede leer éste formato entonces, éste regresará
un mensaje de error durante el desempaquetamiento.
PvmDafalnPlace. Los datos son dejados en su lugar. El buffer sólo contiene el tamaño
y el apuntador de cada elemento ha ser enviados. Cuando pvm-send() es llamado, los
elementos son copiados directamente fuera de la memoria del usuario. Esta opción
disminuye el número de veces que el mensaje es copiado, sólo si el usuario no modifica los
elementos entre el tiempo en que son empaquetados y el tiempo en que éstos son enviados.
Otro uso de esta opción es empaquetar una vez y, modificar y enviar algunos elementos (
arreglos ) múltiples veces durante una aplicación.
int bufid
= pvm-inifsend(inf encoding)
La rutina pvm-inifsend() limpia el buffer de envíos y crea uno para empaquetar un
mensaje nuevo. El esquema de codificación usado por este empaquetamiento es colocado
por encoding. El nuevo identificador de buffer es regresado en bufid. Si el usuario esta
usando un sólo buffer de envíos, entonces la función pvrn-inifsend() debe ser llamada antes
de empaquetar un nuevo mensaje dentro del buffer, de otra manera el mensaje en el buffer
se fusionará con el nuevo mensaje.
Paso de Mensajes 63
3.4
int info = pvm-freebuf(int bufid)
La rutina pvm-freebuf() libera el buffer con identificador bufid. Esto se debe realizar
después, de que el mensaje ha sido enviado y ya no se necesita el buffer. La llamada a la
función pvm-mkbuf() crea un buffer para un nuevo mensaje si se requiere. Ninguna de estas
llamadas es requerida cuando usamos pvm-inifsend(), la cuál realiza estas funciones por el
usuario.
int bufid = pvm-getsbuf(void)
La rutina pvm-getsbut() regresa el identificador del buffer activo de envío de
mensajes.
int bufid = pvm-getrbuf(void)
La rutina pvm-getrbuf() regresa el identificador del buffer activo de recepción de
mensajes.
int oldbuf = pvm-setsbuf( int bufid)
Esta rutina coloca el identificador del buffer de envio activo en bufid, guarda el estado
del buffer previo y regresa su identificador en oldbuf.
int oldbuf = pvm-setrbuf(int bufid)
Esta rutina coloca el identificador del buffer de recepción activo en bufid, guarda el
estado del buffer previo y regresa su identificador en oldbuf
Si bufid es cero en pvm-sefsbuf() Ó pvm-sefrbuf() entonces, se guarda el buffer actual
y no hay buffer activo. Esta innovación puede ser usada para guardar el estado actual de los
mensajes de una aplicación, de ahí que una biblioteca matemática ó una interfaz gráfica, las
cuáles también usan mensajes PVM no interferirán con el estado de los buffers de la
aplicación. Después de que éstas terminan, los buffers de la aplicación pueden ser
reiniciados.
Esto es Útil para enviar mensajes sin reempaquetarlos, usando las rutinas de buffer de
mensajes. Esto es ilustrado por los siguientes fragmentos:
bufid = pvm-recv( src, fag);
oldid = pvm-setsbuf( bufid );
info = pvm-send( dsf, fag );
info = pvm-freebuf(o/did );
64 Aplicaciones bajo PVM
3.4.2 Empaquetamiento de Datos
Cada una de las siguientes rutinas tipo C empaqueta un arreglo de datos, de un tipo
conocido, dentro del buffer activo de envíos. Estas rutinas pueden ser llamadas múltiples
veces para empaquetarse en un sólo mensaje. Así un mensaje puede contener varios
arreglos cada uno con un tipo de datos distinto. No hay límite para la complejidad de los
mensajes empaquetados, pero una aplicación debe desempaquetar los mensajes
exactamente como éstos fueron empaquetados. Las estructuras de C deben ser pasadas
empaquetando sus elementos individuales.
Los argumentos para cada una de las rutinas son un apuntador al primer elemento a
ser empacado, el argumento nitem es el número total de elementos a empaquetar de éste
arreglo y stride es el método a usar cuando empacamos. Una excepción es pvm-pkstr(), la
cuál por definición empaqueta una cadena de caracteres terminada con NULL, de ahí que no
necesita los argumentos nitem Ó stride.
int info = pvm-pkbyte
int info = pvm-pkcplx
int info = pvm-pkdcplx
int info = pvm-pkdouble
int info = pvm-pkfloat
inf info = pvm-pkint
int info = pvm-pklong
int info = pvm-pkuinf
int info = pvm-pkushort
int info = pvm-pkulong
int info = pvm-pklong
int info = pvm-pkshort
int info = pvm-pkstr
int info = pvm-packf
( char
*CPl
( float
*XPl
(double
*ZPJ
(double
*dPl
(float
*fp,
(int
*nPJ
( long
*nPJ
(unsigned int
( unsigned short
( unsigned long
( long
(short
(char *cp)
(const char *fmt, ..
int nitem, int stride)
int nitem, inf striúe)
int nitem, int sfride)
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
*np, int nitem, int stride)
*np, int nitem, int sfride)
*np, inf nitem, int stride)
*np, int nitem, int stride)
*np, int nitem, int stride)
PVM también proporciona una rutina de empaquetamiento pvm-packf() que usa un
formato de expresión prinff-like ( cómo imprimir ) para especificar qué datos se empaquetan
y cómo se empaquetan dentro del buffer de envíos. Todas las variables son pasadas como
direcciones si los argumentos count y stride son especificados; de otra manera se asume
que las variables tienen valor fijo.
3.4.3 Envío y recepción de datos.
int info
= pvm-sená(int
tid, int msgtag);
La rutina pvm-send() etiqueta el mensaje con un identificador entero msgtag y envía
éste inmediatamente al proceso con identificador tid.
int info = pvm-mcast(int *tids, int ntask, int msgtag)
Paso de Mensajes 65
3.4
La rutina pvm-mcasf() etiqueta el mensaje con un identificador entero msgfag y
difunde el mensaje a todos las tareas especificadas en el arreglo de tids ( excepto el mismo
). El arreglo de tids es de longitud nfask.
int info = pvmjsend( int fid, int msgtag, void *vp, int cnt, int type)
La rutina pvm-psend() empaqueta y envía un arreglo de un tipo de datos especificado
a la tarea identificada con tid. En C el argumento type puede tener los siguientes valores:
PVM-STR
PVM-BYTE
PVM-SHORT
PVM-I NT
PVM-LONG
PVM-USHORT
PVM-U LONG
PVM-FLOAT
PVM-CPLX
PVM-DOUBLE
PVM-DCPLX
PVM-U INT
Estos nombres están definidos en pvm3/inc/ude/pvm3.h.
int bufid = pvm-recv( int fid, int msgfag)
Ésta rutina de recibimiento de bloques espera hasta que un mensaje con etiqueta
msgtag llegue desde una tarea con identificador fid. Un -1 en msgtag Ó en fid significa que
todos los mensajes son recibidos no importando su etiqueta Ó fid. Éste coloca el mensaje en
un nuevo buffer de recepción activo que es creado a menos que se haya guardado con una
llamada a pvm-sefrbuf(). Si el buffer de recibimiento ya fue creado previamente es borrado.
int bufid = pvm-nrecv( int fid, int msgtag)
Si el mensaje solicitado no ha llegado, entonces la rutina de recepción de no-bloques
pvm-nrecv() regresará cero en bufid. esta rutina puede ser llamada múltiples veces para el
mismo mensaje, para verificar si éste ha llegado mientras realiza trabajo Útil entre llamadas.
Cuando no puede realizarse trabajo útil, la rutina pvm-recv() de recepción de bloques puede
ser llamada para el mismo mensaje. Si un mensaje con etiqueta msgfag ha llegado desde
una tarea con identificador fid, pvm-nrecv() coloca este mensaje en un nuevo buffer de
recepción activo, el cuál regresa el identificador de este buffer. El buffer de recepción activo
previo es limpiado, si no es que éste ha sido guardado con la llamada a pvm-setribuf(). Un -1
en msgtag Ó en tid significa que todos los mensajes son recibidos no importando su etiqueta
Ó tid.
int bufid = p v m j r o b e ( int fid, int msgtag)
Si el mensaje solicitado no ha llegado, entonces pvm-probe() regresa bufid=O. Sino,
regresa un bufid diferente de cero, pero no es recibido el mensaje. Ésta rutina puede ser
llamada múltiples veces para un mismo mensaje, para verificar si éste ha llegado. Con la
función pvm-bufinfo() y el bufid obtenido se tiene información del mensaje antes de recibirlo.
66 Aplicaciones bajo PVM
int info = pvm-bufinfo( int butid, int *bytes, int *msgtag, int *tid)
int bufid
= pvm-trecv( int tid, int msgtag, struct timeval *tmout)
La rutina pvm-bufinfo() regresa msgtag, el tid de la tarea fuente, y la longitud en bytes
del mensaje identificado por bufid.
PVM proporciona una función de recepción de mensajes a destiempo. Considerar el
caso donde un mensaje nunca va ha llegar (debido a un error o una falla),la rutina
pvm-recv() se bloquearía. Cuando pasa cierta cantidad de tiempo, el usuario ya no espera
recibir el mensaje. La rutina pvm-trecv() permite especificar un periodo de tiempo de
espera. Si el periodo de tiempo de espera es muy grande, pvm-trecv() actuará como
pvm-recv(). Si el periodo de tiempo de espera es cero, actúa como pvm-nrecv(). Asi,
pvm-trecv() llena el intervalo entre las funciones de recepción de bloques y de no-bloques.
int info = pvmjrecv( int tid, int msgtag, void *vp, int cnt,
int type, int *did,int *dag, int *rcnt)
La rutina pvm-precv() combina las funciones de recepción y desempaquetamiento de
un bloque. Esta función no regresa un bufid, regresa el valor actual de tid, msgtag y cnt en
rtid, rtag y rcnt respectivamente.
3.4.4 Desempaquetamiento de datos
Las siguientes rutinas de C desempaquetan varios tipos de datos del buffer de
recepción. En una aplicación deben igualar a sus correspondientes rutinas de
empaquetamiento en tipo, número de elementos y pasos. nitem es el número de elementos
a desempacar y stride son los pasos.
int info = pvm-upkbyte
int info = pvm-upkcplx
int info = pvm-upkdcplx
int info = pvm-upkdouble
int info = pvm-upkfloat
int info = pvm-upkint
int info = pvm-upklong
int info = pvm-upkuint
int info = pvm-upkushort
int info = pvm-upkulong
int info = pvm-upklong
int info = pvm-upkshort
int info = pvm-upkstr
int info = pvm-unpackf
( char
*CPJ
( float
*XP,
( double
*ZPJ
( double
*dp,
(float
*fPf
(int
*nP,
( long
*np,
(unsigned int
( unsigned short
( unsigned long
( long
(short
( char *cp)
(const char *fmt, ...
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
int nitem, int stride)
*np, int nitem, int stride)
*np, int nitem, int stride)
*np, int nitem, int stride)
*np, int nitem, int stride)
*np, int nitem, int stride)
La rutina pvm-unpackf() usa un formato de expresión printf-like ( cómo imprimir ) para
especificar qué y cómo desempaquetar los datos del un buffer de recepción.
Compilando Aplicaciones P V M 67
3.5
3.5 Compilando aplicaciones PVM
Un programa en lenguaje C que se ejecuta bajo PVM, necesita ser ligado con
libpvm3.a. Los programas de ejemplo y el archivo makefile son proporcionados con el código
fuente en el directorio $HOMUpvm3/examples. El archivo Reaúme que se encuentra en ese
directorio, describe como construir y ejecutar los ejemplos. Makefile muestra como las
aplicaciones en C y Fortran deben ser ligados con las bibliotecas de PVM, también contiene
información acerca de las bibliotecas adicionales requeridas para algunas arquitecturas. Un
programa make, independiente de la arquitectura es proporcionado con PVM. Éste script se
encuentra en $HOM€/pvm3/lb/aimk; al ejecutarlo, automáticamente reconocerá la clase de
arquitectura en la que se encuentra y en base a ello agregará las bibliotecas correctas.
Para construir cualquiera de los ejemplos:
Ir al directorio $HOMUpvm3/lib.
Teclear:
% aimk $HOMUpvm3/examples/nombre~ejemplo
Se creará el programa ejecutable y aimk lo colocará en:
$HOMUpvm3/bin/P VM- ARCH
Para compilar tu programa tienes que moverlo al directorio:
$HOMUpvm3/bin/P VM-A RCH
y teclear:
% cc nombre-programa. c libpvm.a -o nombre-ejecutable
y se creará el programa ejecutable
nombre-ejecutable
3.6 Métodos de Depuración
En general, la depuración de programas paralelos es mucho más difícil que la
depuración de programas seriales. No solamente son varios programas ejecutándose
simultáneamente, si no que al interactuar pueden generar errores. Por ejemplo un proceso
puede recibir un dato erróneo que posteriormente cause problemas, como la división por
cero. Otro ejemplo es deadlock ( estancamiento ), donde un error de programación causa
que todos los tareas esperen mensajes. Todas las rutinas PVM devuelven una condición de
error, si algún error ha sido detectado durante su ejecución.
68 Aplicaciones bajo P V M
A continuación presentamos una lista de los códigos de error y su significado.
Código de error
Significado
Por default PVM imprime las condiciones de error detectadas en las rutinas PVM. La
rutina pvm-setopt() permite habilitar la opción de reporte automático. Imprime el diagnostico
de las tareas inicializadas, y es visualizado usando el redireccionamiento de la consola o por
la llamada a pvm-catchouf() en la tarea inicializada (frecuentemente la tarea maestro).
pvm-catchout() causa que la salida estándar de todas las tareas inicializadas
subsecuentemente aparezca en la salida estándar de la tarea inicial.
Las tareas PVM pueden ser iniciadas manualmente bajo cualquier depurador serial
estándar, por ejemplo úbx.stúouf, su salida estándar siempre aparece en la ventana en la
que fueron comenzadas.
. Las tareas PVM que son generadas con spawn pueden ser iniciadas bajo un
depurador, colocando la opción flag, para incluir a PvmTaskDebug en la llamada
pvm-spawn(), por default PVM ejecutará el script PVM_ROOT/lib/debugger. Éste script se
inicializa en una ventana xterm en el host donde se inició PVM y las tareas son ejecutadas
bajo un depurador en esta ventana. La tarea que es depurada, puede ser ejecutada en
cualquiera de los hosts en la máquina virtual, especificando los argumentos flag y where de
pvm-spawn(). El usuario puede crear su script de depuración y personalizarlo incluyendo el
depurador de su preferencia Ó aún mejor, un depurador paralelo si éste está disponible. El
usuario puede decir a PVM donde se encuentra este script usando la opción (bx=) en el
Hosffile.
Métodos de Depuración 69
3.6
Los mensajes de diagnostico enviados a sfúerr desde una tarea no aparecerán en la
pantalla del usuario. Todos serán mandados a un archivo de la forma /fmp/pvml.<uid> en el
host donde fue inicializado PVM. Los mensajes de sfdouf pueden aparecer en este archivo.
Las tareas que son inicializadas desde la consola PVM pueden tener sus sfdouf(y todas las
sfúouf de sus tareas hijos) redirecionadas a la ventana de la consola o en un archivo
separado.
La rutina pvm-sefopf() permite al usuario el uso de un conjunto de máscaras de
depuración que mandan su salida a /fmp/pvml.<uid>. Por default el nivel de depuración es:
"sin mensajes del depurador". El nivel de depuración puede ser cambiado varias veces
dentro de una aplicación. Los mensajes del depurador describen solamente qué está
realizando PVM y no que aplicación se está ejecutando.
Los tres pasos recomendados para depurar programas son:
0
0
Primero, si es posible ejecute el programa como un proceso sencillo y depúrelo
como cualquier programa serial. El propósito de este paso es detectar errores
lógicos y señalarlos cómo no relacionados al paralelismo.
Segundo, ejecute el programa usando 2 a 4 tareas en una sola máquina. PVM
ejecutará estos tareas como multitareas. El propósito de este paso es verificar la
sintaxis de comunicación y la lógica, Por ejemplo en un programa se utilizó una
etiqueta de mensaje igual con 5 en el envío, pero el receptor espera por un
mensaje con etiqueta igual a 4. Un error muy común descubierto en este paso, es
el uso de etiquetas de mensaje que no son únicas. Para ilustrar esto, imagine que
una misma etiqueta es siempre utilizada. Un proceso recibe algún dato inicial en
tres mensajes separados, pero no tiene manera de determinar cuál de los
mensajes tiene qué datos. PVM regresa cualquier mensaje que iguale la solicitud
de la fuente y la etiqueta, esto es hasta que el usuario se asegura que este par
identifica en forma única el contenido del mensaje. Los errores de etiquetas
duplicadas son muy difíciles de depurar, porque esto es sensitivo para efectos de
sincronización y podrían no ser reproducibles de ejecución en ejecución. Si el error
no puede ser determinado por el código de error de PVM ó en un mensaje,
entonces el usuario puede obtener un control completo del depurador en su
programa, iniciando una o todas sus tareas bajo el depurador.
El tercer paso es ejecutar los mismos 2 a 4 tareas en varias máquinas. El propósito
de este paso es verificar los errores de sincronización que se han producido por los
retardos de la red. La clase de errores frecuentemente descubiertos en este paso
son sensibles al algoritmo de orden de los mensajes recibidos, y programas que se
estancan debido a errores lógicos sensitivos a los retardos de la red. Otra vez el
control completo del depurador puede ser obtenido en este paso, pero puede no
ser Útil, porque el depurador puede pasar por alto o enmascarar errores que
aparecerán posteriormente
70 Aplicaciones bajo P V M
3.7 Ejecutando aplicaciones PVM.
Una vez que PVM es ejecutado, una aplicación que usa rutinas PVM puede ser
inicializada desde el prompt de comandos de UNIX, en cualquiera de los hosts de la
máquina virtual. Una aplicación no necesita ser inicializada en la misma máquina que el
usuario elige para iniciar PVM.
Stdouf y stderr aparecen en la pantalla para todas las tareas PVM inicializadas
manualmente' . Los mensajes de error producidos por las tareas son escritos en el archivo
de errores estándar, /tmp/pvmd.aid > en el host donde PVM fue inicializado. La forma
más fácil de ver la salida estándar de las tareas PVM es usando un cambio en la dirección
de salida, que está disponible en la consola PVM2. Si la salida esfándar no es
redireccionada en la consola PVM, entonces esta salida también irá a el archivo
/fmp/pvmd.a i d >.
Cuando los usuarios quieren ejecutar sus programas con un valor que es menor a la
prioridad que tienen, sobrecargan menos las estaciones de trabajo. Hay un par de formas
para realizar esto. El primer método, inicia tu programa en un script. Este es un ejemplo de
un script de dos líneas:
$!/bin/sh
exec nice -10tu-programa $*
Entonces cuando se produce el script, ejecutará el programa en el nivel deseado. El
segundo método es llamando a la función UNIX setphrity(' en tu programa. No es necesario
iniciar una nueva sesión PVM para cada aplicación que se quiera ejecutar, aunque será
necesario reiniciar PVM si un proceso falla.
I
2
Hay tareas que no son inicializadas manualmente, P.e. en los programas maestrdesclavo.
Spawn, con la opción ->.
CAPíTULO 4
La cena de los filósofos
4.1 Planteamiento.
En 1965, Dijsktra planteo y resolvió un problema de sincronización llamado el
problema de la cena de los filósofos. Desde entonces, todas las personas que idean cierta
primitiva de sincronización intentan demostrar lo maravilloso de la nueva primitiva al mostrar
su elegancia para resolver el problema de la cena de los filósofos. El problema se puede
enunciar de la manera siguiente.
Cinco filósofos se sientan a la mesa. Cada uno tiene un plato de espagueti. El
espagueti es tan escurridizo, que un filosofo necesita dos tenedores para comerlo. Entre
cada dos platos hay un tenedor.
La vida de un filosofo consta de periodos alternados de comer y pensar. (Esto es una
abstracción, incluso para los filósofos, pero las demás actividades son
irrelevantes en este caso). Cuando un filosofo tiene hambre, intenta obtener un tenedor para
su mano izquierda y otro para su mano derecha, alcanzando uno a la vez y en cualquier
orden. Si logra obtener los dos tenedores, come un rato y después deja los tenedores y
continua pensando. La pregunta clave es: se puede escribir un programa para cada filosofo
que lleve a cabo lo que se supone debería y nunca se detenga?.
Para sistemas con un procesador o con memoria compartida se tiene una solución, la
cual no tiene bloqueos ni inanición, es correcta y permite el máximo paralelismo para un
numero arbitrario de filósofos. Utiliza un arreglo, state, para llevar un registro de la actividad
de un filosofo: si esta pensando, comiendo o hambriento (intenta obtener los tenedores ). Un
filosofo puede comer solo si los vecinos no están comiendo. Los vecinos del i-esimo filosofo
se definen en las macros LEFT y RIGHT. En otras palabras, si i=2, entonces LEFT=I y
RIGHT=3.
El programa utiliza un arreglo de semáforos, uno por cada filosofo, de manera que los
filósofos hambrientos pueden bloquearse si os tenedores necesarios están ocupados.
Observe que cada proceso ejecuta el procedimiento philosopher como código principal, pero
71
72
La Cena de los Filósofos
los demás procedimientos, take-forks, putforks y test son procedimientos ordinarios y no
procesos separados. (Tanembaum, 1993)
#include "prototypes.h"
5
#define N
#define LEFT (i-l)%N
#define RIGHT (i+l)%N
O
#define THINKING
#define HUNGRY
1
#define EATING
2
typedef int semaphore;
int state[N];
semaphore mutex=l;
semaphore s[N];
/* numero de filósofos */
/* numero del vecino izquierdo de i */
/* numero del vecino derecho de i */
i* el filosofo esta pensando */
/* el filosofo intenta conseguir los tenedores */
/* el filosofo esta comiendo */
/* los semáforos son un caso particular de int */
/* arreglo para llenar un registro del estado de cada quien */
i* exclusión mutua para las regiones criticas *I
i* un semáforo por filosofo */
void philosopher (int ¡) {
while (TRUE){
think();
take-forks(¡);
ea0
put-forks(¡);
/* i: de cual filosofo se trata ( desde O hasta N-1 ) */
/* se repite por siempre */
/* el filosofo esta pensando */
/* obtiene dos tenedores o se bloquea */
/* mmm..., espagueti! *I
i* coloca los tenedores en la mesa */
1
}
void take-forks(int i)
{
down(&mutex);
state[i]=HUNGRY;
test(¡);
up(&mutex);
down(&s[i]);
/* i: de cual filosofo se trata ( desde O hasta N-I ) */
void put-forks(int i)
{
down(&mutex);
state[¡] = THINKING;
test(LEFT);
test(R1GHT);
up(&mutex);
/* i: de cual filosofo se trata ( desde O hasta N-1 ) */
1
/* entra a la región critica *I
/* registra el hecho de que el filosofo i tiene hambre */
/* intenta tomar 2 tenedores *I
/* sale de la región critica */
/* se bloquea si no consiguió los tenedores */
/* entra a la región critica */
/* el filosofo ha terminado de comer *I
/* ve si el vecino izquierdo puede comer ahora */
/* ve si el vecino derecho puede comer ahora *I
i* sale de la región critica *I
1
/* i: de cual filosofo se trata ( desde O hasta N-1 */
void test(int i)
{
if state[i]=HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
{
1
state[¡] == EATING;
up(&s[il);
Implantación en un Sistema Distribuido usando PVM 73
4.2
4.2 Implantación en un Sistema Distribuido usando PVM
Pensemos ahora en que tenemos un conjunto de hosts conectados mediante una red
local, cualquier host puede obtener información de los demás por medio de paso de
mensajes.
Imaginemos ahora, que podemos conectar a esta red en un anillo virtual(Fig.4.1), en
donde cada host representa un filosofo, entonces tenemos una representación virtual de una
mesa en donde se tienen a los filósofos esperando comer, cada uno de ellos tiene conexión
directa a sus vecinos derecho e izquierdo, pero nuestro problema ahora tiene un cambio: es
un sistema distribuido real, es decir, sin memoria compartida.
M.-Maestro.
F.- FilOSOfO.
S.-Recibe Solicitudes.
E.- Estados.
Anillo Virtual.
Ahora debemos replantear nuestro problema. No podemos implementar semáforos
para que cada filosofo pueda entrar a su región critica, porque como hemos visto en el
Capitulo 1 debemos usar de manejo de regiones criticas para sistemas distribuidos.
Para implantar la cena de los filosos comensales usamos el modelo maestro/esclavo
con comunicación entre esclavos, donde el programa produce y direcciona cinco filósofos
esclavos, los cuales se comunican e informan al maestro cuando han logrado entrar a la
región critica. El maestro les envía una lista de todos los tids de los esclavos y un arreglo con
el nombre de los hosts en el anillo, posteriormente cada filosofo crea a su vez dos procesos
hijos, uno llamado RECIBE-SOLICITUDES y otro llamado ESTADOS los cuales tienen la
función de procesar la solicitud de exclusión mutua y de mantener actualizado el estado del
filosofo ( si esta comiendo, pensando o con hambre ) respectivamente. El programa está
74 La Cena de los Filósofos
basado en la solución que dio Dijsktra haciendo las modificaciones para un sistema
distribuido.
Para lograr la exclusión mutua en un sistema distribuido, nos basamos en el algoritmo
distribuido propuesto por Glenn Ricart y Agrawala que analizamos en la sección 1.2.3.2
El algoritmo envía solo 2*(N-1) mensajes (de ida y vuelta))=, donde N es el número de
nodos en la red por invocación a la sección critica. Aquí cabe hacer una aclaración ya que el
algoritmo envía una solicitud de entrada a todos los nodos de la red y sólo en el caso de que
reciba autorización de todos, puede entrar a la sección critica, en nuestro caso esto no es
necesario ya que para que un filosofo pueda entrar a la región critica es necesario sólo que
sus vecinos derecho e izquierdo le den la autorización para entrar, de ahí que si tenemos
cinco filósofos dos pueden estar dentro de la región critica, por lo cual mediante álgebra
sencilla podemos ver que es necesario solo 4 mensajes por invocación, para N filósofos.
Dentro del algoritmo de exclusión mutua se usa un semáforo binario para lograr
exclusión mutua de una base de datos compartida en éste host, el semáforo fue implantado
usando mecanismos de comunicación entre procesos( inter process comunication, IPC). El
mecanismo IPC de semáforos implantado en el UNlX System V es una generalización del
concepto de semáforo descrito por Dijkstra, ya que va a permitir manejar un conjunto de
semáforos mediante el uso de un identificador asociado. También vamos a poder realizar
operaciones signal y waif que actualizan de manera atómica todos los semáforos asociados
bajo un mismo identificador. Un semáforo en UNlX System V se compone de los siguientes
elementos:
0
El valor del semáforo.
El identificador del Último proceso que manipuló el semáforo.
El número de procesos que hay esperando a que un valor del semáforo se incremente.
El número de procesos que hay esperando a que el semáforo tome el valor de cero.
La base de datos compartida se implantó usando igualmente mecanismos IPC. La
memoria convencional que puede direccionar un proceso a través de su espacio de
direcciones virtuales es local a ese proceso y cualquier intento de direccionar esa memoria
desde otro proceso va a provocar una violación de segmento. Para solucionar este
problema, UNlX System V brinda la posibilidad de crear zonas de memoria con la
característica de poder ser direccionada por varios procesos simultáneamente. Esta memoria
va a ser virtual, por lo que sus direcciones físicas asociadas podrán variar con el tiempo.
Esto no va a plantear ningún problema, ya que los procesos no generan direcciones físicas,
sino virtuales, y es el Kernel el encargado de traducir de unas a otras( Márquez, 1994).
Presentamos el algoritmo a continuación.
Implantación en un Sistema Distribuido usando P V M 75
4.2
.....................................................................................................
I*
MAESTRO
I*
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
#include "pvm3.h"
#include <header.h>
void main(void){
int val;
int mytid;
int tids[NFILO];
int who;
int msgtag0=0;
int msgtag1=1;
int estadistica[NFILO];
I
*I
*I
I
/* bandera para checar cuantos procesos se crearon "1
I* identificador de tarea para el maestro *I
I* arreglo de tids para un esclavo filoso *I
I* que filosofo reporta su estado "1
i^ etiqueta del proceso maestro *I
1" etiqueta de fin de procesos *I
r" estadistica de cuantas veces se ha entrado a la *I
P sección de critica *I
I* del for "1
I* arreglo auxiliar *I
int i;
int tids2[1];
char **args;
char *host[NFILO];
I* arreglo para los hosts *I
I* apuntador a un arreglo de tareas *I
args=(char **)O;
host[O]="iris50';host[l]="iris51 host[2]="ir¡s52";
host[3]="iris53";host[4]="iris54";
I* asigna un tid al maestro *I
mytid=pvm-m *id();
for(i=O;i<NFILO;i++){
val=pvm_spawn(ESCLAVE_FILO,args,PvmTaskHost,host[i],1
,&tids2); i^ crea un filosofo por host *I
if(val==1)
tids[i]=tids2[1];
else
printf("No se inicio el filoso %d en %s \n",i,host[i]);
'I;
1
printf("Posiblemente Iniciaron todos los filósofos");
r" crea el buffer de envío *I
pvm-initsend(PvmDataRaw);
I* empaqueta el arreglo de tids *I
pvm-pkint(tids,NFILO,l);
P empaqueta el arreglo de hosts *I
pvm-pkbyte( host,NFILO,1);
pvm-mcast(tids,NFILO,msgtagO);i^ envía los datos empacados a los filósofos *I
I* ESPERA RESULTADOS DE LOS FIL&3OFOS *I
for(i==O;i<lTEFW;i++){
pvm-recv(-1 ,-1);
pvm_upkint(&who,l,l);
estadistica[who]=estadistica[who]+l
;
printf(" El filosofo %d ha comido %d veces \n",who,estadistica[who]);}
pvmexit();
1
76
La Cena de los Filósofos
........................................................................................................ I
I*
FILOSOFO MAiN
*I
I*
*I
........................................................................................................ I
#include "pvm3.h"
#include "header.h"
void main(void){
I* para el tid del padre *I
int parent;
I* etiqueta para recibir los datos del padre *I
int msgtag0=0;
int msgtag8=8;
I* del for *I
int i;
P que filosofo soy *I
int me;
I* tid de cada proceso ESTADO *I
int dest;
I* tid para cada proceso RECIBE-SOLICITUDES *I
int recibe;
I* tid del filosofo *I
int mytid;
I* arreglo de tids de los filosofos *I
int tids[NFILO];
P arreglo de host *I
char host[NFILO];
P arreglo de tids de los procesos ESTADO *I
int estado-tids[NFILO];
I* arreglo de tids de los procesos RECIBE-SOLICITUDES *I
int solicitudes-tids[NFILO];
I* validaciones *I
int va1,vall;
char **args;
args=(char **)O;
I* determina que filosofo soy *I
mytid=pvm-mytid;
I* obtiene el tid del padre *I
parent=pvm-parent();
I* recibe datos del padre *I
pvm-recv(parent,msgtagO);
I* desempaqueta el arreglo de tids *I
pvm-upkint(tids,NFILO, 1);
I* desempaqueta el arreglo de hosts*/
pvm-upkbyte(host, NFILO,1);
P busca que filosofo soy *I
for(i=O;i<NFILO;i++){
if(mytid==tids[i]){
/* es el filosofo i *I
me=¡; break;}
1
I*
CREA UN PROCESO RECIBE-SOLICITUDES PARA ESTE HOST *I
val=pvm~spawn(RECIBE~SOLICITUDES,args,PvmTaskHost,
host[me],l ,&recibe);
I* CREA UN PROCESO ESTADOS PARA ESTE HOST *I
val 1=pvm-spawn( ESTADOS,args,PvmTaskHost,host[me],1,&dest);
if(val!=l)
printf("No se inicio el proceso recibe-solicitudes en %s \n",host[me]);
if(val1!=l)
printf("No se inicio el proceso estados en %s\n",host[me]);
I* empaqueta el arreglo de tids *I
pvm-pkint(tids,NFILO,l );
I* empaqueta quien soy *I
pvm-pkint(&me,l ,l);
P se envían al proceso RECIBE-SOLICITUDES *I
pvm-send(reci be,msgtag8);
llena-estado-tids(me,tids,dest,estado-tids);
llena-solici-tids(me,tids,recibe,solicitudes-tids);
philosopher(me,dest,tids,estado-tidslsolicitudes-tids);
1
Implantación en un Sistema Distribuido usando P V M 77
4.2
......................................................................................................... I
I*
*I
I*
DOWN
*I
I*
*I
I*
*I
....................................................................................................... I
down(int semid, struct sembuf operacion, int Sharet-vars) {
I* el semáforo que hay que modificar *I
operacion.sem-num=Sharet-vars;
I* como se le asigna un valor negativo significa *I
operacionsem-op=-1 ;
I* que se realizara una operacion wait *I
operacion sem-flag=O;
i" bandera *I
i" semop realiza operaciones atómicas sobre los *I
semop(semid,&operacion,1);
I* semáforos que hay asociados bajo el *I
I* identificador semid *I
1
........................................................................................................
I*
I*
I*
I*
UP
I
*I
*I
*I
*I
I
........................................................................................................
up(int semid, struct sembuf operacion, int Sharet-vars) {
operacion.sem-num=Sharet-vars;
I* el semáforo que hay que modificar *I
operacion.sem-op=l ;
I* como se le asigna un valor positivo significa *I
I* que se realizara una operacion signal *I
operacion sem-flag=O;
I* bandera *I
semop(semid,&operacion,1);
I* semop realiza operaciones atómicas sobre los *I
I*
semáforos que hay asociados bajo el *I
i" identificador semid *I
1
.....................................................
I
I*
*I
I*
LLENA ESTADO TlDS
*I
I*
*I
*I
I*
........................................................................................................ I
void Ilenaestado-tids(int me,int *tids, int dest, int *estado-tids){
int msgtag7=7;
P'etiqueta *I
int i;
i" del for *I
int who;
i" quien da su tid *I
estado-tids[me]=dest;
P'lleno mi lugar en el arreglo *I
pvm-pkint(&dest,l ,l);
I* empaqueto el tid de mi proceso estado *I
pvm-pkint(&me,l ,l);
I* empaqueta quien soy *I
pvm~mcast(tids,NFILO,msgtag7);
i" envío a todos los procesos el tid menos a mi *I
i" mismo *I
.....................
ESPERA ELTID DE LOS DEMAS PROCESOS *****************I
for(i=O;i<NFILO-1 ;i++){
pvm-recv(-1 ,msgtag7);
pvm-upkint(&dest, 1,l);
pvm-upkint(&who, 1,í);
estado-tids[who]=dest ;
I* espero NFILO-1 mensajes *I
I* recibo de cualquier proceso *I
I* desempaqueto el tid del proceso estado *I
/* desempaqueto quien me lo manda *I
I* lleno el arreglo con el tid *I
78 L a Cena de los Filósofos
........................................................................................................ I
I*
/*
LLENA SOLICITUD TlDS
I*
I*
........................................................................................................
*I
*I
*/
*I
I
void llena-solicitids(int me,int *tids,int recibe,int *solicitudes-tids){
P etiqueta *I
int msgtag6=6;
I* del for *I
int i;
I* quien da su tid *I
int who;
I* lleno mi lugar en el arreglo *I
solicitudes-tids[me]=recibe;
i* empaqueto el tid de mi proceso solicitud *I
pvm-pkint(&recibe, 1,l);
P empaqueta quien soy *I
pvm-pkint(&me, 1,l);
P envío a todos los procesos el tid menos a *I
pvm-mcast(tids, NFIL0,msgtagG);
I* mi mismo *I
.....................
ESPERA ELTID DE LOS DEMAS PROCESOS *****************I
for(i=O;i<NFILO-1 ;i++){
pvm-recv(-1 ,msgtag6);
pvm-upkint(&recibe,l ,l);
pvm-u pkint (&who,1,1);
solicitudes-tids[who]=reci
be;
1
1
I* espero NFILO-1 mensajes *I
I*
recibo de cualquier proceso *I
P desempaqueto el tid del proceso estado *I
P desempaqueto quien me lo manda *I
I* lleno el arreglo con el tid *I
........................................................................................................ /
/*
I*
/*
I*
RECIBE RESPUESTAS
.......................................................................................................
*/
*I
*I
*I
I
void recibe-respuestas(int me,int *solicitudes-tids)(
I* vecino derecho *I
int right;
I* vecino izquierdo *I
int left;
int msgtag3=3;
I* etiqueta *I
I* bandera de solicitud *I
int solicitud=4;
P respuesta enviada por el vecino izquierdo *I
int respuecta-I;
/* respuesta enviada por el vecino derecho *I
int respuesta-r;
I* vecino derecho modulo NFILO *I
right=(i+l)%NFILO;
I* vecino izquierdo modulo NFILO *I
left=(¡-l)%NFILO;
I********* ESPERA PERMISO PARA ENTRAR DEL FILOSOFO IZQUIERDO*************/
pvm~recv(solicitudes[leftJ,msgtag3);
pvm-upkint(respuesta-I, 1);
I*********ESPERA PERMISO PARA ENTRAR DEL FILOSOFO DERECHO **************I
pvm~recv(solicitudes[right],msgtag3);
pvm-upkint(respuesta-r, 1);
1
4.2
Implantación en un Sistema Distribuido usando P V M 79
.........................................................................................................
I*
I*
ENTRA EXCLUSION MUTUA
I*
I
I
*I
*I
*I
I
DataBase *entraexclusion-mutua(int me,int *tids,int *solicitudes-tids)P entra a la región critica *I
/* vecino derecho *I
int right;
I* vecino izquierdo *I
int left;
int msgtag3=3;
P etiqueta *I
P identificador del semáforo *I
int semid;
I* identificador de la memoria compartida *I
int shmid;
i* bloque de memoria compartida *I
int memory;
I* apuntador a la memoria compartida *I
DataBase *Datos;
I* estructura que indica las operaciones *I
struct sembuf operacion;
I* que vamos a llevar a *I
I* cabo sobre el semáforo *I
I*
es la llave que indica a que grupo de *I
key-t llave;
P semáforos queremos acceder *I
I* tipo semáforo *I
typedef int semaphore;
P semáforo binario que garantiza que solo un *I
semaphore Shared-va rs;
I* proceso accesa a las variables compartidas */
Ilave=ftok("archivo",k) ;
I* Creación de una llave para acceder a los *I
/* mecanismos de comunicación entre procesos *I
P (Interprocess comunication, iPC)*/
semid==semget(llave,l ,IPCCREAT I 0600); I* petición de un identificador para el semáforo *I
I* con permisos de lectura y escritura para el
I* usuario *I
memory=sizeof(DataBase);
I* determina el tamaño de la memoria compartida *I
shmid=shmget(lPC~PRIVATE,rnemory,lPC~CREAT~O600);
P identificador de la memoria *I
if(shmid==-1)(perror("error al crear la memoria");exit(-1);)
if(semid==-1)
I* no se creo el identificador *I
{perror("error en la asignación semget");
exit(-1) ;}
I* salimos del proceso con este error *I
semctl(semid,Shared-vars,SElVAL,1);
I* Inicialización del semáforo *I
down(semid,operaciones,S
hared-vars);
*)-l);l* unión de la zona de direcciones I*
if(Datos==(DataBase *)shmat(shmid,O,O)==(DataBase
I* compartida nuestro espacio de *I
I* direcciones virtuales *I
{perror("error al unir la memoria compartida");exit(-1);)
Datos->shmid=shmid;
Datos->sect-critica=TRUE;
I* quiere entrar a la sección critica *I
Datos->secu-del-nodo=Datos->secu_mac_alta+l;
/* obtiene la secuencia mas alta */
up(semid,operaciones,Shared-vars);
right=(¡+ 1)%NFILO;
i" vecino derecho modulo NFILO *I
left=(i-l)%NFILO;
P vecino izquierdo modulo NFILO *I
pvm-initsend(PvmDataRaw);
I*
crea el bufer de envío *I
pvm-pkint(&Datos->secu-del-nodo,1,l);
I* empaqueta la secuencia del nodo *I
pvm-pkint(&me,l ,l);
I*
empaqueta quien soy *I
pvm~pstruc(Datos,5,1);
i" empaqueta los datos de memoria compartida*/
pvm~send(solicitudes~tids[left],msgtag3); P envía la solicitud al filosofo izquierdo *I
pvm~send(solicitudes~tids[right],msgtag3);I* envía la solicitud al filosofo derecho *I
recibe-respuestas( me,solicitudes-tids);
P recibe a autorización de entrada *I
return (Datos);
1
80
La Cena de los Filósofos
........................................................................................................ /
/*
*/
/*
SALE EXCLUSION MUTUA
*/
/*
*I
/*
*/
........................................................................................................ /
void sale-exclusion-mutua(int me, int &tids,DataBase *Datos){ i" sale de la región critica */
/* vecino izquierdo */
int lefi = (me-l)%NFILO;
/* vecino derecho */
int right = (me+l)%NFILO;
i" etiqueta de respuesta */
int respuesta=7;
int msgtag3=3;
/* etiqueta */
i" sale de la región critica */
Datos->sect-critica=FALSE;
i" si se marco para entrar después a la *I
if(Datos->arreglo-resp[lefi]) {
i" región critica al vecino izquierdo *I
/* desmarca el hecho que espera respuesta *I
Datos->arreglo-resp[lefi]=FALSE;
/* se empaqueta la respuesta */
pvm-pkint(&respuesta, 1,l);
/* este filosofo manda respuesta a vecino */
pvrn-~end(tids[left],msgtag3);
/* izquierdo */
1
/* si se marco para entrar después a la *I
/* región critica al vecino derecho *I
Datos->arreglo-resp[right]=FALSE; i" desmarca el hecho que espera respuesta */
/* se empaqueta la respuesta */
pvm-pkint(&respuesta,l ,I);
I* este filosofo manda respuesta a vecino */
pvm-send(tids[right] ,msgtag3);
/* derecho */
if(Datos->arreglo-resp[right])
{
1
........................................................................................................ /
/*
*/
/*
CAMBIA ESTADO
*I
I*
*/
/*
*I
........................................................................................................ /
int cambia-estado(int state,+int solicitud, int dest, int who){
int msgtag2=2;
i" crea el bufer de envío */
pvm-initsend(PvmDataRaw);
i" quien solicita *I
pvm-pkint(&who,l , I ) ;
/* empaqueta el estado a cambiar *I
pvm-pkint(&state,l ,I);
/* empaqueta la solicitud */
pvm-pkint(&solicitud, 1,l);
/* envía el estado a dest */
pvm-send(dest, msgtag2);
if(solicitud==TRUE){
I***************
ESPERA EL RESULTADO DE LA SOLICITUD ......................
pvm-recv(dest, msgtag2);
pvm-upkint(&state,l ,I);
return( *state );
1
1
/
I* solo de destino recibe */
I* desempaqueta el estado */
i" regresa el estado solicitado */
4.2
Implantación en un Sistema Distribuido usando P V M 81
........................................................................................................ I
*
*I
I*
TEST
*I
I*
*I
I*
*I
...................................................................................................... I
void test(int i,int *tids,int *estado-tids){
/* vecino derecho *I
int right;
I* vecino izquierdo *I
int leít;
/* estado del i-esimo filosofo *I
int estado-¡;
/* estado del filosofo izquierdo *I
int estado-I;
I* estado del filosofo derecho *I
int estador;
/* vecino derecho modulo NFILO *I
right=(i+l)%NFILO;
i* vecino izquierdo modulo NFILO *I
left=(¡-l)%NFILO;
I* que estado tengo *I
estado~i=cambia~estado(-2,TRUE,estado~tids[i],tids[i]);
estado~l=cambia~estado(-2,TRUE,estado~tids[le~],tids[leít]);
/* que estado tiene el filosofo left *I
estado~r=cambia~estado(-2,TRUE,estado~tids[right],tids[right]);/*
que estado tiene el filosofo derecho *I
if(estado-¡==HUNGRY && estado-l!=EATING && estado-r!=EATING)
cambia~estado(EATING,FALSE,estado~tids[i],tids[i]);
I* el filosofo puede comer *I
1
I
I
*/
TAKE FORKS
*I
*I
*I
..................................................................................................... /
DataBase *take-forks(int me,int dest,int *tids,int *estado-tids,int *solicitudes-tids){ I* obtiene dos *I
DataBase *Datos;
/* tenedores *I
Datos=entra_exclusion-mutua(me,tids,solicitudes-tids)
I* entra a la región critica *I
cambiaestado(HUNGRY,FALSE,dest,O);
I* el filosofo me tiene hambre *I
I* intenta tomar dos tenedores *I
test(me,tidc,estado-tids);
sale-exclusion-mutua(me,tids,Datos);
/* sale de la región critica *I
return Datos;
I*
/*
I*
I*
1
....................................................................................................... I
*I
I*
I*
PUT FORKS
*I
*I
I*
*I
I*
........................................................................................................ I
coloca los dos *I
void put-forks(int me,int dest,int *tids,int *estadotids,int *solicitudestids){ I*
/*tenedores *I
int right;
I* vecino derecho *I
I* vecino izquierdo *I
int left;
I* memoria compartida *I
DataBase "Datos;
right=(me+1)%NFILO;
/* vecino derecho modulo NFILO *I
left=(me-l)%NFILO;
I* vecino izquierdo modulo NFILO *I
Datos=entra-exclusion_mutua(me,tids,solicitudes-tids) I*
entra a la región critica *I
I*
cambiaestado(THINKING,FALSE,dest,O);
el filosofo me tiene hambre *I
test(right,tids,estado-tids);
test(left,tidc,estado-tids);
sale-exclusion-mutua(me,tids,Datos);
1
I* sale de la región critica *I
82
La Cena de los Filósofos
........................................................................................................ /
/*
*I
I*
PHILOSOPHER
*I
I*
*/
I*
*I
........................................................................................................
/
void philosopher(int me,int dest,int *tids,int *estado-tids,int *solicitudes-tids);
DataBase *Datos;
int j=O;
while(j<lTERA){
cambiaestado(THINKING,FALSE,dest,O);
/* el filosofo me esta pensando */
Datos=take_forks(me,dest,tids,estado~tids,solicitudes~tids););
I* obtiene dos tenedores o se
i* bloquea */
i* el filosofo me esta comiendo */
cambia-estado( EATING,FALSE,dest,O);
put-forks(me,dest,tids,estado-tids,solicitudes-tids); i* regresa ambos tenedores */
shmcti(Datos-xhmid,IPC-RMI D,O);
/* borrado de la zona de memoria compartida */
j++;
1
pvm-halt();
1
/*terminar con todos */
........................................................................................................
I*
RECIBE-SOLI CI TUDES MAIN
/
*I
........................................................................................................ /
#include "pvm3.h"
#include "header.h"
void main(void){
recibe-solicitudes();
1
4.2
Implantación en un Sistema Distribuido usando P V M 83
......................................................................................................
I*
I*
I*
I*
RECIBE SOLlCl TUDES
........................................................................................................
I
*I
*I
*I
*I
I
void recibe-solicitudes(void){
I* bandera de respuesta */
int respuesta=7;
i* es verdadera si no se puede contestar inmediatamente*/
int diferir;
I* tid del filosofo padre *I
int parent;
int msgtag3=3;
I* etiqueta *I
I* etiqueta *I
int msgtag8=8;
i* quien hace la solicitud para entrar a la región critica *I
int who;
i* bandera de exclusión mutua *I
int mutex;
I* de que i-esimo filosofo soy hijo *I
int me;
I* arreglo de tids de los filósofos *I
int tids[NFILO];
I* la secuencia del nodo que hace la solicitud *I
int k;
DataBase *Datos;
parent=pvm-parent();
I* obtiene el tid del proceso padre *I
pvm-recv(parent, msgtag8);
I* recibe datos del proceso padre (filosofo ¡)*I
pvm-upkint(tids,NFILO, 1);
I* recibo el arreglo de tids *I
pvm-upkint(&me, l,l,);
I* recibo el numero de filosofo de quien soy hijo *I
pvm-recv(-I ,msgtag3);
I* recibe solicitudes de cualquier proceso *I
pvm-upkint(&k, 1,l);
I*
desempaqueta la secuencia de quien hace la solicitud *I
pvm-upkint(&who,l,l ,);
/* desempaqueta quien hace la solicitud */
pvm~upstruck(Datos,5,1);
i* recibe los datos de la zona de memoria compartida *I
Datos->secu-mas-alta=Maximo( me,tids,Datos->secu-mas-alta,k);l* obtiene la máxima secuencia en toda la
red *I
down(mutex);
I* obtiene la autorización para entrar a la sección critica *I
diferir=Datos->secc-critica&& ((k>Datos->secu_del-nodo)lI(k==Datos->secudeI_nodo
&& who>me));
I* obtiene permiso para entrar a la sección critica en *I
I*
la red *I
up (mutex);
i^ sale de la sección critica de este host *I
if (diferir){
I* si diferir es TRUE no puedo entrar a la sección critica *I
I* de la red *I
Datos->arreglo-resp[who]=TRUE;
I*
lo marco para contestarle posteriormente *I
1
else{
pvm-pkint(&respuesta,l ,l);
r' autorizo a who entrar a la sección critica de la red *I
pvm~send(tids[who],msgtag3);
1
1
84 La Cena de los Filósofos
........................................................................................................ /
/*
*/
/*
MAXI MO
*/
/*
*/
/*
*/
........................................................................................................
I
int maximo(int me,int *tids,int Datos->secu-mas-alta){
i" etiqueta */
int msgtag9=9;
E* del for */
int i;
i" quien solicita */
int who;
I* secuencia de quien solicita */
int secuwho;
I* empaqueta quien es */
pvm-pkint(&me,l ,l);
pvm-pkint(&Datos->secu-mas-alta, 1,I); i" empaqueta la secuencia mas alta */
i" envía la información a todos */
pv-mcast(tids, NFILO,msgtag9);
for(¡=O; i<NFILO;i++){
/* recibe la secuencia de los demás */
pvm-recv(-1 ,msgtag9);
I* desempaqueta quien la envió *I
pvm-upkint(&who, 1,l);
/* desempaqueta la secuencia */
pvm-upkint(secu-who, 1,l);
if(secu-who>Datos->secu-mas-alta) I* verifica la mas alta de todos *I
Datos->secu-mas-aIta=secuwho;
1
return(Datos->secu-mas-alta);
1
........................................................................................................
I*
/*
ESTADOS
I*
/
*/
*/
*/
/*
*/
........................................................................................................ /
void estado(void){
I* de quien recibe la solicitud */
int who;
/* etiqueta de solicitud */
int solicitud;
I* estado del filosofo *I
static int Estado;
I* etiqueta */
int msgtag2=2;
I* se ejecuta por siempre */
for(; ;I{
i" recibe de cualquiera *I
pvm-recv(-1 ,msgtag2);
/* desempaqueta quién envia */
pvm-upkint(&who,l ,I);
/* desempaqueta la variable estado */
pvm-upkint(&state,l , I ) ;
/* desempaqueta la solicitud */
pvm-upkint(&solicitud, 1,I);
r' solicitud no es TRUE cuando solo */
if(solicitud!=TRUE)
I* se desea cambiar a un nuevo estado */
/* Estado ahora tiene el nuevo estado *I
Estado=state;
/* solicitud es FALSE cuando se desea *I
else{
/* que se mande el estado del filosofo *I
I* crea un buffer de envío */
pvm-initsend(PvmDataRaw);
I* empaqueta el estado del filosofo *I
pvm-pkint(&Estado,l ,I);
/* envía el estado a quien lo solicitó *I
pvm~send(who,msgtag2);}
1
1
main(){
estado();}
/* procedimiento principal */
Implantación en un Sistema Distribuido usando P V M 85
4.2
HEADER.H
#include <stdio.h >
#include <sys/types.h >
#include <sys/ipc.h >
#include <sys/sem.h >
#include <sys/shm.h >
#define
##define
#define
#define
#define
#define
#define
#define
#define
#define
#define
ESCLAVE-FILO
RECIBE-SOLICITUDES
ESTADOS
NFILO
ITEM
END
FALSE
TRUE
THINKINK
EATING
HUNGRY
typedef struct data{
int secu-del-nodo=O;
int secu-mas-alta=O;
int secc-critica;
int arreglo-resp [NFILO];
int Share-vars=TRUE;
int shmid;
}Dataease;
"slave-filo"
"recibe-sol"
"estados"
5
20
2
O
1
2
3
4
P archivo esclavo */
r archivo recibe-sol
*/
r' archivo estados *I
/* numero de filososfos */
P iteraciones */
P bandera de termino*/
r falso */
r^ verdadero *I
/* pensando *I
/* comiendo */
Phambre */
/* estructura de datos compartidos */
BIBLIOGRAFÍA
0
BEN-ARI, M., “Principles of Concurrent and Distribuited Programming”, la
Edición.
Editorial Prentice Hall, Gran Bretaña, 1990.
O
COMER, D. , “TCPAP, Principios basicos, protocolos y arquitectura”, 3a Edición. Editorial
Prentice Hall, México 1996.
O
0
O
DAY,J.D., y ZIMMERMAN, H., ” The OS1 Reference Model” , Proc. of the IEEE, vol. 71,
Diciembre, 1983
DEITHEL, “Introducción a los Sistemas Operativos”, 2a. Edición. Editorial Addison-Wesley,
U.S.A, 1993
HAHN, H., “UNIX sin fronteras”, la. Edición. Editorial Mc. Graw Hill, México 1995.
LAMPORT, L., “Time Clocks, and the Ordering of Events in a Distribuited System” ,
Commun of the ACM, vol. 21, pp. 558-564, Julio 1978
O
MARQUEZ G., F., “Unix , Programación Avanzada”, 2a Edición. Editorial Addison-Wesley
Iberoamericana, Wilmington, Delaware, E.U.A., 1994.
O
MILAN, MILENKOVIC, “Sistemas Operativos: Conceptos y Diseño”, la.
Edición. Editorial
Mc. Graw Hill, España, 1988
0
NEMETH, E., ”Unix System Administration Handbook”, Editorial Addison Wesley, 1995
0
PARKER, SYBIL P., “Diccionario Mc. Graw Hill de Computación Bilingüe English-Spanish”,
la.
Edición. Editorial Mc. Graw Hill, México, 1984
O
PVM MANUAL REFENCE. ( hffp://www.netlib2.cs.utk.edu
)
0
RICART, G. y AGRAWALA, A. K., “An Optimal Algoritm for Mutual Exclution in Computer
Networks”, Commun. of the ACM, vol. 24, pp. 9-17, Enero 1981
O
STEVENS, RICHARD W., “Advanced Programming in the UNIX Environment”, 12a.
Edición. Editorial Addison-Wesley, Massachusetts, USA, 1996
O
TANEMBAUM, ANDREW S., “Sistemas Operativos Modernos”, la.
Edición en español.
Editorial Prentice Hall, México, 1993
0
TANEMBAUM, ANDREW S., ”Sistemas Operativos Distribuidos”, 1a. Edición en español.
Editorial Prentice Hall, México, 1995
0‘
TANEMBAUM, ANDREW S., “Redes de Ordenadores”, la.
Edición en español. Editorial
Prentice Hall, México, 1992
86

Documentos relacionados