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
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