Búsqueda personalizada

viernes, 18 de enero de 2008

Funciones Pvm

a) Control de procesos

pvm_mytid():Esta función incorpora a la PVM al proceso que la invoca, propor

cionándole un número de tarea único en toda la máquina virtual,

denominado tid. Esta función debe ser la primera rutina que se in

voque antes de cualquier otra.

pvm_exit(): Comunica al demonio local que el proceso está abandonando la

PVM. Esta primitiva no elimina el proceso en sí, sino que éste

continúa pero como un proceso convencional de UNIX.

pvm_spawn():Inicializa un conjunto de tareas que se incorporarán a la PVM.

Para determinar en qué EP se van a crear estas nuevas tareas, la

heurística utilizada podría estar diseñada en función de las medi

das de carga de los EP de la PVM y de la capacidad de cómputo

de los mismos. La función devuelve el número de tareas creadas

en una o más máquinas de la configuración.

Si las tareas han sido inicializadas correctamente, como pará

metro de salida, devuelve un vector con los tids y en caso que

alguna tarea no haya podido ser inicializada, devuelve en su

correspondiente componente del vector, un código de error,

indicando el motivo por el que no ha podido iniciarse dicha

tarea.

pvm_kill(): Elimina de la PVM la tarea identificada por el parámetro tid.

b) Información de la máquina virtual

pvm_parent(): Devuelve el tid del proceso que creó la tarea que invoca a esta

función. Si existe algún error devuelve un error tipo

PvmNoParent.

pvm_pstat(): Devuelve el estado de una tarea PVM identificada por tid.

Devuelve PvmOk si la tarea está en ejecución, PvmNoTask si no

lo está, y PvmBadParam si el tid es incorrecto.

pvm_mstat(): Devuelve PvmOk si el EP está ejecutando tareas PVM,

PvmHostFail si no es posible acceder al EP, o PvmNoHost si ese

EP no está en la PVM. Esta función es útil en el diseño de

aplicaciones tolerantes a fallos. En todo momento se puede

interrogar por el estado de cualquiera de las máquinas

involucradas en la maquina virtual

pvm_config(): Devuelve información acerca de la PVM, incluyendo el número

de EPs, tipo de arquitecturas, etc.

pvm_tasks(): Devuelve información sobre las tareas en ejecución en la PVM.

c) Configuraciones dinámicas

pvm_addhost (), pvm_delhost () :Estas primitivas permiten añadir o eliminar

un EP de la configuración inicial de la PVM.

d) Primitivas de señalización

pvm_sendsig(): Envía la señal signum a otra tarea PVM identificada por un tid.

pvm_notify(): Provoca el envío de un mensaje a una serie de tareas

especificadas, al ocurrir un evento en la PVM. Los posibles

eventos que pueden ocurrir son: una tarea ha finalizado, una

máquina ha caído o ha sido eliminada, o si se ha añadido una

nueva máquina.

e) Primitivas de mensajes de error

pvm_error(): Imprime el estado de error de la última primitiva PVM.

pvm_serror(): Seleccionar esta rutina permite enviar mensajes de error de

forma automática, y de esta forma cualquier error que se

produzca, automáticamente notificará el mensaje de error

asociado.

f) Envío y recepción de mensajes

El envío de mensajes conlleva los pasos siguientes:

· Se debe inicializar un buffer de envío a través de una primitiva particular.

· Los datos que van a ser enviados deben ser empaquetados (primitivas de

empaquetado).

· El mensaje se envía a otro proceso (primitivas de emisión).

Un mensaje podrá ser recibido a través de una primitiva de recepción con o sin

bloqueo. Después se debe desempaquetar cada elemento del mensaje. En la PVM, sólo existe un buffer de envío y otro de recepción activos por cada proceso en un instante dado. El usuario es responsable de gestionar qué tipo de mensaje está activo en cada momento, pudiendo crear los que sean necesarios.

pvm_mkbuf(): Crea un buffer de envío vacío y especifica un código para

designarlo. Existen varias opciones para la codificación de

los datos en este mensaje, dependiendo del valor de la

constante encoding:

· PvmDataDefault. Codificación estándar XDR.

· PvmDataRaw. No realizar codificación.

· PvmDataInPlace. Los datos se quedan en el mismo EP.

pvm_initsend(): Limpia el buffer de envío y lo prepara para empaquetar

nuevos datos. Esta primitiva debe ejecutarse antes de

empaquetar los datos. Los valores de encoding son los

mismos que en la función anterior.

pvm_freebuf(): Elimina el buffer identificado por el parámetro bufid. Se

debe ejecutar esta primitiva siempre que se deje de utilizar

este buffer.

pvm_getsbuf(): Devuelve el número del buffer de envío activo en ese

momento.

pvm_getrbuf(): Devuelve el número del buffer de recepción activo.

pvm_setsbuf(): Selecciona el nuevo buffer de envío activo y devuelve el

identificador del anterior.

pvm_setrbuf(): Selecciona el nuevo buffer de recepción activo, retornando el

identificador del anterior. El empaquetado de los datos se

realiza con diferentes primitivas que empaquetan un

conjunto de elementos del mismo tipo en el buffer de envío

activo. Estas primitivas pueden ser llamadas múltiples veces

y en cualquier orden. De esta forma, un mensaje puede

contener varios vectores de datos de distintos tipos. No

existen restricciones relativas a cuan complejos pueden ser

estos mensajes. La única condición que existe es que se

deben desempaquetar en el mismo orden en que fueron em

paquetados. Se dispone de primitivas para empaquetar

bytes, enteros, reales, complejos, caracteres, etc.

El envío y recepción de datos se lleva a cabo con las siguientes primitivas.

pvm_send(): Esta primitiva etiqueta un mensaje con un valor entero,

msgtag, que indica un número de mensaje, y lo envía a una

tarea especificada por un tid.

pvm_mcast(): Etiqueta el mensaje con un identificador entero, msgtag, y lo

envía a las tareas especificadas por tids.

pvm_nrecv(): Recepción sin bloqueo. Si el mensaje solicitado no ha sido

recibido, esta primitiva devuelve un cero. Esta rutina puede

ser invocada repetidamente para solicitar el mensaje y

comprobar si ha llegado. Mientras el mensaje llega, se

puede seguir realizando otro trabajo entre dichas llamadas.

Si llega un mensaje con etiqueta específica desde una tarea

tid concreta, entonces se colocará el mensaje en el buffer

activo.

pvm_recv(): Primitiva de recepción con bloqueo. Espera hasta que un

mensaje con la etiqueta especificada y/o con tid

determinado haya llegado.

pvm_bufinfo(): Devuelve información acerca del mensaje con el

identificador especificado.

pvm_recvf(): Uso de definiciones propias de la primitiva de recepción de

mensajes.

g) Primitivas de grupos de procesos

Estas funciones permiten controlar un conjunto de procesos que por cuestiones del problema a resolver, interesa que estén agrupadas. Cuestiones como la sincronización de los procesos, se pueden realizar permitiendo que todos los procesos pertenezcan a un grupo. Dentro de un grupo, se pueden realizar diferentes funciones que aquí comentamos. Cualquier tarea PVM puede incorporarse o abandonar un grupo de procesos en cualquier momento sin tener que informar al resto de tareas del grupo.

pvm_joingroup (): Permite que una tarea se incorpore al grupo. Crea un

grupo con el nombre especificado y asigna la tarea a ese

grupo. A cada tarea, al incorporarse a un grupo, se le

asigna un número, a través del cual se gestionan los

grupos.

pvm_lvgroup(): Una tarea abandona un grupo invocando a esta primitiva.

En caso de volver a incorporarse, el número asignado

será posiblemente distinto al que tenía anteriormente.

Los números se asignan dinámicamente.

pvm_gettid(): Devuelve el tid del proceso que está en un grupo con un

número determinado.

pvm_getinst(): Devuelve el número que tiene una tarea dentro de un grupo.

pvm_gsize(): Devuelve el número de miembros que existen en un grupo.

pvm_barrier(): Cuando se invoca a esta función, el proceso se bloquea en

espera de que todas las tareas implicada ejecuten esta

función.

pvm_cast(): Etiqueta un mensaje con un identificador entero, y envía el

mensaje a todas las tareas de un grupo determinado. Si una

tarea se incorpora a un grupo durante la llegada de este

tipo de mensajes, podría no recibirlo.

Pvm Informacion General

La herramienta Pvm, es un conjunto de librerias necesarias para trabajar con el concepto de clusters y crear una maquina paralela virtual. Para que Pvm funcione necesariamente tiene que estar funcionando la red de computadores, permitiendo la conexión de los mismos y el inicio de sesiones en hosts remotos.

La maquina paralela virtual es una maquina que no existe, pero un API apropiado nos permite programar como si existiese. El modelo abstracto que nos permite usar el API de la Pvm consiste en una maquina multiprocesador completamente escalable (es decir, que podemos aumentar y disminuir el numero de procesadores en caliente, esto en cualquier momento). Para ello, nos va a ocultar la red que estemos empleando para conectar nuestras maquinas, asi como las maquinas de la red y sus caracteristicas especificas.

Demonio Pvm

La arquitectura de la pvm se compone de dos partes. La primera parte es el demonio, llamado Pvmd. El demonio ha de estar funcionando y configurado en todas las maquinas que vayan a compartir sus recursos computacionales con la maquina paralela virtual. A diferencia de otros demonios y programas del sistema, el demonio de Pvm puede ser instalado por el usuario en su directorio particular (de hecho, la instalacion por defecto es asi). Esto nos va a permitir hacer supercomputacion como usuarios, sin tener que discutir con el administrador de la red que programas vamos a poder ejecutar. Una vez que un usuario instalo en un directorio Pvm todo los usuarios pueden hacer uso de esa instalacion con el requisito de que el directorio donde esté instalada Pvm sea de lectura al usuario que quiera hacer uso de ella.

El demonio Pvmd es el responsable de la maquina virtual de por sí, es decir, de que se ejecuten nuestros programas para Pvm y de gerenciar los mecanismos de comunicación entre máquinas, la conversión automatica de datos y de ocultar la red al programador. Por ello, una vez que Pvm esté en marcha, el paralelismo es independiente de la arquitectura de la maquina, y solo depende de la arquitectura ed la maquina virtual creada por Pvm. Esto nos va a evitar el problema que teniamos con los Sockets, ya que teniamos que hacer una rutina de codificacion y otra de decodificacion, al menos, por cada arquitectura distinta del sistema.

Cada usuario, arrancará el demonio como si de un programa normal se tratase, para ejecutar el codigo de Pvm. Este programa se queda residente, realizando las funciones anteriores.

Biblioteca de desarrollo

La segunda parte es la biblioteca de desarrollo. Contiene las rutinas para operar con los procesos, transmitir mensajes entre procesadores y alterar las propiedades de la maquina virtual. Toda aplicación se ha de enlazar a la biblioteca para poderse ejecutar después. Tendremos tres ficheros de bibliotecas, la libpvm3.a (biblioteca basica en C), la libgpvm3.a (biblioteca de tratamiento de grupos) y la libfpvm3.a (biblioteca para fortran)

Tareas

Un programa para Pvm va a ser un conjunto de tareas que cooperan entre si. Las tareas se van a intercambiar información empleando paso de mensajes. Pvm, de forma transparente al programador, nos va a ocultar las transformaciones de tipos asociadas al paso de mensajes entre maquinas heterogeneas. Toda tarea de Pvm puede incluir o eliminar maquinas, arrancar o parar otras tareas, mandar datos a otras tareas o sincronizarse con ellas.

TIDs

Cada tarea en Pvm tiene un número que lo identifica inequívocamente, denominado TID (Task Identification Number). Es el numero al que se mandan los mensajes habitualmente. Sin embargo, no es el unico metodo de referenciar una tarea en Pvm. Muchas aplicaciones paralelas necesitan hacer el mismo conjunto de acciones sobre un conjunto de tareas. Por ello, Pvm incluye una abstracción nueva, el grupo

Grupos

Un grupo es un conjunto de tareas a las que nos podemos referir con el mismo codigo, el identificador de grupo. Para que una tarea entre o salga de un grupo, basta con avisar de la salida o entrada al grupo. Esto nos va a dotar de un mecanismo muy comodo y potente para realizar programas empleando modelos SIMD (Single Instruction, Multiple Data), en el que vamos a dividir nuestros datos en muchos datos pequeños que sean faciles de tratar, y después vamos a codificar la operación simple y replicarla tantas veces como datos unitarios tengamos de vivir el problema. Para trabajar con grupos, ademas de enlazar la biblioteca de Pvm (libpvm3.a) tenemos que enlazar tambien la de grupos (libgpvm3.a)

Funcionamiento

Habitualmente para arrancar un programa para Pvm, se lanzará manualmente desde un ordenador contenido en el conjunto de maquinas una tarea madre. La tarea se lanzará con el comando spawn desde un monitor de la maquina virtual, que a su vez se activará con el comando pvm. Esta tarea se encargará de iniciar todas las demás tareas, bien desde su funcion main (que va a ser la primera en ejecutarse), bien desde alguna subrutina invocada por ella. Para lanzar nuevas tareas se emplea la funcion pvm‗spawn, que devolverá un código de error, asociado a si pudo o no crearla, y el TID de la nueva tarea.

Clases de Arquitectura

Para evitar el problema de andar realizando transformaciones continuas de datos, Pvm define clases de arquitectura. Si es la misma, no necesita convertir datos, con lo que se tiene un gran incremento en el rendimiento. En caso que sean distintas las clases de arquitectura se emplea el protocolo XDR para codificar el mensaje.

Las clases de arquitectura están mapeadas en números de codificacion de datos, que son los que realmente se transmiten y, por lo tanto, los que realmente determinan la necesidad de la conversión

Paso de Mensajes

El modelo de paso de mensajes es transparente a la arquitectura para el programador, por la comprobación de las clases de arquitectura y la posterior codificación con XDR de no coincidir las arquitecturas. Los mensajes son etiquetados al ser enviados con un número entero definido por el usuario, y pueden ser seleccionados por el receptor tanto por dirección de origen como por el valor de la etiqueta.

El envío de mensajes no es bloqueante. Esto quiere decir que el que envía el mensaje no tiene que esperar a que el mensaje llegue, sino que solamente espera a que el mensaje sea puesto en la cola de mensajes. La cola de mensajes, además, asegura que los mensajes de una misma tarea llegarán en orden entre si. Esto no es trivial, ya que empleando UDP puede que enviemos dos mensajes y que lleguen fuera de orden (UDP es un protocolo no orientado a conexión). TCP, por ser un protocolo orientado a la conexión, realiza una reordenación de los mensajes antes de pasarlos a la capa superior, sin embargo, tiene el inconveniente que establecer las conexiones entre nodos empleando TCP supone, si tenemos n nodos, tendremos un mínimo de

($n)(n$-1) conexiones TCP activas. Provocando esto que hasta para números ridículos de $n$ nos quedamos sin puertos por éste planteamiento. Establecer conexiones TCP entre procesos en lugar de entre nodos es peor todavía, por las mismas razones que en el caso de los nodos.

La comunicación de las tareas con el daemon se hace empleando TCP. Esto se debe a que, al ser comunicaciones locales, la carga derivada de la apertura y cierre de un canal es muy pequeño. Además, no vamos a tener tantas conexiones como en el caso de la conexión entre daemons, ya que las tareas no se conectan entre sí ni con nada fuera del nodo, por lo que sólo hablan directamente con su daemon. Esto determina que serán n conexiones TCP, que sí es una cifra razonable.

La recepción de los mensajes podemos hacerla mediante primitivas bloqueantes, no bloqueantes o con un tiempo máximo de espera. La PVM nos dotará de primitivas para realizar los tres tipos de recepción. En principio nos serán más cómodas las bloqueantes, ya que nos darán un mecanismo de sincronización bastante cómodo. Las de tiempo máximo de espera nos serán útiles para trabajar con ellas como si fuesen bloqueantes, mas dando soporte al hecho de que puede que el que tiene que mandarnos el mensaje se haya colgado. Por último, la recepción de mensajes mediante primitivas no bloqueantes hace de la sincronización un dolor de cabeza. De cualquier forma, en los tres casos anteriormente citados la misma PVM se encargará de decirnos cuándo una tarea acabó. Para informarnos de lo que pasa, emplea un mecanismo de eventos asíncronos.

La PVM puede ser empleada de forma nativa como funciones en C y en C++, y como procedimientos en Fortran. Basta para ello con tomar las cabeceras necesarias (si trabajamos con C o C++); y, para los tres, enlazar con la biblioteca adecuada, que viene con la distribución estándar. En el caso de C es libpvm3.a y en el del Fortran libfpvm3.a.

Si deseamos trabajar en otros lenguajes puede ser un poco más complejo. Si el lenguaje permite incorporar funciones nativas en lenguaje C (como es el caso, por ejemplo, de Java) no hay ningún problema; ya que podemos invocar la función; bien directamente si el lenguaje lo permite, bien haciendo alguna pequeña rutina para adaptar el tipo de los datos, el formato de llamada a función o cualquiera de las restricciones que nos imponga el lenguaje que empleemos para invocar funciones en C.

Hemos de destacar que toda función en C pvm_alguna cosa tiene como equivalente en Fortran pvmfalgunacosa, y viceversa.

El programa PVM corresponde al interprete de comandos de nuestra máquina virtual. Algunos de los comandos más importantes son:

* add máquina: Incorpora la máquina indicada a la máquina paralela virtual.

* delete máquina: Elimina la máquina indicada del conjunto de máquinas

asociadas a la máquina paralela virtual. Como es lógico, no podremos

eliminar la máquina desde la que estamos ejecutando el interprete de

comandos.

* conf: Configuración actual de la máquina paralela virtual.

* ps: Listado de procesos de la máquina paralela virtual. ps -a lista todos los

procesos.

* halt: Apaga la máquina paralela virtual. Esto significa que mata todas las

tareas de la PVM, elimina el daemon de forma ordenada y sale del

programa pvm.

* help: Lista los comandos del programa.

* id: Imprime el TID de la consola.

* jobs: Genera un listado de los trabajos en ejecución.

* kill: Mata un proceso de la PVM.

* mstat: Muestra el estado de una máquina de las pertenecientes a la PVM.

* pstat: Muestra el estado de un proceso de los pertenecientes a la PVM.

* quit: Sale de la máquina paralela virtual sin apagarla.

* reset: Inicializa la máquina. Eso supone matar todos los procesos de la PVM

salvo los programas monitores en ejecuciónón, limpiar las colas de

mensajes y las tablas internas y pasar a modo de espera todos los

servidores.

* setenv: Lista todas las variables de entorno del sistema.

* sig señal tarea: Manda una señal a una tarea.

* spawn: Arranca una aplicación bajo PVM. Es un comando bastante

complejo cuyas opciones veremos en una sección aparte.

* trace: Actualiza o visualiza la máscara de eventos traceados.

* alias: Define un alias predefinido, es decir, un atajo para teclear un

comando.

* unalias: Elimina un alias predefinido.

* version: Imprime la versión usada de la PVM