miércoles, 24 de octubre de 2012

PVM (Parallel Virtual Machine, Maquina Virtual Paralela)

 Consiste en un software y un conjunto de librerias, que permiten establecer una coleccion de uno o mas sistemas de computacion, con el fin de poder integrar dichos sistemas en un esquema de una sola maquina virtual.

Opera sobre diferentes plataformas de UNIX y Windows. Puede establecerse en cualquier esquema de red heterogenea, sobretodo en el ambiente de Internet.

Brinda rutinas en lenguajes C y en Fortran para procesos como paso de mensajes asincronicamente y para el control de procesos. Ademas de ofrecer un paralelismo escalable, entre otras muchas ventajas.

Es muy utilizado para realizar y desarrollar aplicaciones cientificas complejas que requieren un esquema de programacion en paralelo, dada su compatibilidad entre sistemas operativos, es muy portable y facil de instalar.

Actualmente PVM esta en la version 3.4.6. Pero para exponer los ejemplos nos basaremos en la version 3.0.

Ventajas

Es una de las librerías de paso de mensajes más fáciles de usar.

Flexible: Control arbitrario de dependencia de estructuras.

 La aplicación decide:
  • Donde y cuando ejecutar o terminar las tareas.
  • Que maquinas se añaden o se eliminan desde la máquina virtual paralela.
  • Que tareas se pueden comunicar y/o sincronizar con otras.

Desventajas

Al ser un esquema heterogéneo de ordenadores, el rendimiento depende de la capacidad de procesamiento de los ordenadores vinculados al esquema de la máquina virtual (lo que era una ventaja tiene una su desventaja).

Es algo deficiente en cuanto al paso de mensajes se refiere.

Configuración

Para configurar la MV utilizamos el programa “pvm” en cualquiera de los ordenadores que forman parte de la MV.

Nos aparecerá un promt donde podremos introducir comandos para interactuar con la MV.

Los comandos mas interesantes son:
  • add/delete hostname: Añade/Quita el ordenador con nombre hostname a la maquina virtual.
  • conf: Para ver la configuracion actual.
  • ps –a: Para ver los procesos en ejecución en la maquina virtual.
  • quit: Para salir de la consola pero dejar la maquina virtual activa.
  • halt: Para detener la maquina virtual y salir de la consola.

Áreas de Aplicación

  • Simulación a gran escala.
  • Computación en tiempo crítico.
  • Diseño e ingeniería asistida por computadora.
  • La industria manufacturera.
  • Visualización de datos científicos.
  • Tecnología de interfase hombre-computadora.


Ejemplos de Programas PVM.

  Ejemplo de Fork Join.
/*
    Ejemplo de Fork Join
    Demuestra como engendrar proceso e intercambiar mensajes.
*/

/* definiciones y prototipos para la libreria de PVM  */
#include <pvm3.h>

/* Numero maximo de hijos que engendrara este programa  */
#define MAXNCHILD   20
/* Etiqueta que usara el menaje de join */
#define JOINTAG     11

int
main(int argc, char* argv[])
{

    /* numero de tareas que seran engendradas, se utiliza 3 como default  */
    int ntask = 3;
    /* codigo que regresan las llamadas PVM */
    int info;
    /* mi identificador de tarea */
    int mytid;
    /* el identificador de tarea de mi padre */
    int myparent;
    /* un arreglo con los identificadores de tarea de los hijos */
    int child[MAXNCHILD];
    int i, mydata, buf, len, tag, tid;

    /* se busca mi identificador de tarea */
    mytid = pvm_mytid();

    /* se evalua si hay error */
    if (mytid < 0) {
        /* si hay error, se imprime */
        pvm_perror(argv[0]);
        /* fin del programa .. bye!*/
        return -1;
        }
    /* se busca el identificador de tarea de mi padre  */
    myparent = pvm_parent();

    /* finaliza si encuentra algun error diferente de PvmNoParent */
    if ((myparent < 0) && (myparent != PvmNoParent)) {
        pvm_perror(argv[0]);
        pvm_exit();
        return -1;
        }

    /* si no tengo un padre ... yo soy el padre  */
    if (myparent == PvmNoParent) {
        /* cuantas tareas se van a engendrar? */
        if (argc == 2) ntask = atoi(argv[1]);
        /* aseguramos que ntask sea correcto  */
        if ((ntask < 1) || (ntask > MAXNCHILD)) { pvm_exit(); return 0; }

        /* engendramos las tareas de los hijos */
        info = pvm_spawn(argv[0], (char**)0, PvmTaskDefault, (char*)0,
            ntask, child);
        /* se imprimen los identificadores de los hijos */
        for (i = 0; i < ntask; i++)
            if (child[i] < 0) /* imprimir el codigo de error en decimal */
                printf(" %d", child[i]);
            else  /* imprimir el identificador en hexadecimal  */
                printf("t%x\t", child[i]);
        putchar('\n');

        /* Asegurarnos de que se engendro con exito  */
        if (info == 0) { pvm_exit(); return -1; }

        /* Solo se esperaran respuestas de los hijos que se engendraron
           correctamente */
        ntask = info;
  
        for (i = 0; i < ntask; i++) {
            /* recibe un mensaje de cualquier proceso hijo  */
            buf = pvm_recv(-1, JOINTAG);
            if (buf < 0) pvm_perror("calling recv");
            info = pvm_bufinfo(buf, &len, &tag, &tid);
            if (info < 0) pvm_perror("calling pvm_bufinfo");
            info = pvm_upkint(&mydata, 1, 1);
            if (info < 0) pvm_perror("calling pvm_upkint");
            if (mydata != tid) printf("This should not happen!\n");
            printf("Length %d, Tag %d, Tid t%x\n", len, tag, tid);
            }
        pvm_exit();
        return 0;
        }

   /* soy un hijo ... */
   info = pvm_initsend(PvmDataDefault);
   if (info < 0) {
      pvm_perror("calling pvm_initsend"); pvm_exit(); return -1;
      }
   info = pvm_pkint(&mytid, 1, 1);
   if (info < 0) {
      pvm_perror("calling pvm_pkint"); pvm_exit(); return -1;
      }
   info = pvm_send(myparent, JOINTAG);
   if (info < 0) {
      pvm_perror("calling pvm_send"); pvm_exit(); return -1;
      }
   pvm_exit();
   return 0;
}
        La siguiente figura muestra la salida de correr FORK - JOIN. Notese que el orden en que se recibieron los mensajes no es deterministico. Dado que el ciclo principal del proceso padre es del tipo FIRST-COME FIRST-SERVE, el orden de las salidas es determinado por el tiempo que toma a los mensajes viajar de las tareas hijo al padre.

 % forkjoin
 t10001c t40149  tc0037
 Length 4, Tag 11, Tid t40149
 Length 4, Tag 11, Tid tc0037
 Length 4, Tag 11, Tid t10001c
 % forkjoin 4
 t10001e t10001d t4014b  tc0038
 Length 4, Tag 11, Tid t4014b
 Length 4, Tag 11, Tid tc0038
 Length 4, Tag 11, Tid t10001d
 Length 4, Tag 11, Tid t10001e

Ejemplo de Kill.

/*
    Ejemplo del uso de Kill
    Demuestra como saber si las tareas han finalizado
*/

/* definiciones y prototipos de la libreria PVM */
#include <pvm3.h>

/* Numero maximo de hijos que el programa engendrara  */
#define MAXNCHILD   20
/* Etiqueta que usara la tarea para mandar el mensaje de finalizacion  */
#define TASKDIED        11

int
main(int argc, char* argv[])
{

    /* numero de tareas a engendrar, utiliza 3 como default  */
    int ntask = 3;
    /* codigo que regresa de las llamadas PVM  */
    int info;
    /* mi identificador de tarea */
    int mytid;
    /* el identificador de tarea de mi padre */
    int myparent;
    /* arreglo de identificadores de tareas de los hijos  */
    int child[MAXNCHILD];
    int i, deadtid;
    int tid;
    char *argv[5];

    /* se busca mi indentificador de tarea  */
    mytid = pvm_mytid();

    /* se verifica que no exista error */
    if (mytid < 0) {
        /* se imprime el error  */
        pvm_perror(argv[0]);
        /* se termina el programa  */
        return -1;
        }
    /* se busca el identificador de tarea de mi padre */
    myparent = pvm_parent();

    /* finaliza si existe algun error diferente de  PvmNoParent */
    if ((myparent < 0) && (myparent != PvmNoParent)) {
        pvm_perror(argv[0]);
        pvm_exit();
        return -1;
        }

    /* si no tengo padre ... entonces yo soy el padre */
    if (myparent == PvmNoParent) {
        /* cuantas tareas voy a engendrar?  */
        if (argc == 2) ntask = atoi(argv[1]);
        /* se asegura que ntask es valido  */
        if ((ntask < 1) || (ntask > MAXNCHILD))
         { pvm_exit(); return 0; }

        /* se engendran las tareas  */
        info = pvm_spawn(argv[0], (char**)0, PvmTaskDebug, (char*)0,
            ntask, child);

        /* nos aseguramos que no ocurrio un error  */
        if (info != ntask) { pvm_exit(); return -1; }

        /* se imprimen los identificadores de tarea  */
        for (i = 0; i < ntask; i++)
            printf("t%x\t",child[i]); putchar('\n');

        /* se pide la notificacion cuando un hijo termina  */
        info = pvm_notify(PvmTaskExit, TASKDIED, ntask, child);
        if (info < 0) { pvm_perror("notify"); pvm_exit(); return -1; }

        /* matamos al hijo de en medio  */
        info = pvm_kill(child[ntask/2]);
        if (info < 0) { pvm_perror("kill"); pvm_exit(); return -1; }

        /* se espera por una notificacion  */
        info = pvm_recv(-1, TASKDIED);
        if (info < 0) { pvm_perror("recv"); pvm_exit(); return -1; }
        info = pvm_upkint(&deadtid, 1, 1);
        if (info < 0) pvm_perror("calling pvm_upkint");

        /* debe de ser el hijo de en medio  */
        printf("Task t%x has exited.\n", deadtid);
        printf("Task t%x is middle child.\n", child[ntask/2]);
        pvm_exit();
        return 0;
        }

    /* soy un hijo ... */
    sleep(63);
    pvm_exit();
    return 0;
}

No hay comentarios:

Publicar un comentario