Die Kommunikation innerhalb der virtuellen Maschine erfolgt durch den
Austausch von Nachrichten ( Message Passing) zwischen den Tasks
bzw. den Dämonen.
Zu diesem Zweck existieren in jeder Task entsprechende
Message Buffer.
Vor dem Versenden einer Nachricht muß dieser Platz allokiert werden,
dabei ist die
Größe des Puffers jedoch noch nicht festgelegt.
Bei der Initialisierung dieses Puffers ( pvm_initsend()) wird die
Kodierung der Elemente dieser Nachricht festgelegt:
Der Default ist
XDR -Kodierung
( Extended Data Representation), da zu diesem Zeitpunkt nicht
feststeht,
an welche Architektur die Nachricht innerhalb eines heterogenen Netzes
verschickt werden soll.
Es besteht auch die Möglichkeit, unkodierte Nachrichten zu versenden.
Nachdem der Puffer vom Dämon zur Verfügung gestellt wurde, können
durch die Packroutinen
beliebige elementare Datentypen zu einer Message zusammengestellt werden.
Diese können dann mit speziellen Packroutinen
(z.B. pvm_pkint(),
pvm_pkdouble(),
pvm_pkstr())
oder mit Hilfe eines Format-String
( pvm_packf()) in den
Sende-Puffer kopiert werden.
Durch pvm_send() wird der Pufferinhalt an eine andere Task
verschickt, wobei man
der Message noch einen Typ mitgeben kann.
pvm_mcast() verschickt die Nachricht an eine Reihe von Tasks,
deren
TIDs
in einem Vektor übergeben werden.
Im allgemeinen Fall werden die Nachrichten zwischen zwei Tasks über die
PVM -Dämonen
auf den entsprechenden Hosts geroutet.
Für wiederholte Kommunikation besteht die Option, daß die Nachrichten
direkt zwischen zwei Tasks verschickt werden, dieses ist jedoch nur für
eine begrenzte Zahl von Kommunikationskanälen möglich.
Die Nachrichten werden durch den Dämon auf der Empfängerseite
gepuffert und können
von einer Task mittels pvm_recv() abgefragt werden.
Dabei kann gezielt auf eine Nachricht eines bestimmten Typs, eines
bestimmten Absenders
oder auf eine beliebige Nachricht für diese Task reagiert werden.
Die einzelnen Komponenten der Nachricht müssen in derselben
Reihenfolge,
in der sie vor dem Verschicken in den Puffer kopiert wurden, durch die
entsprechenden Routinen
aus dem Empfänger-Puffer wieder ausgelesen werden.
Neben dem blockierenden Empfangen gibt es noch nicht-blockierendes
Empfangen ( pvm_nrecv())
und Empfang mit Time-out ( pvm_trecv()).
Das Verschicken einer Nachricht
unter
PVM
ist immer asynchron, da die Nachrichten nach Aufruf einer
Sende-Routine an den entsprechenden Dämon übergeben werden.
Die Sende-Task fährt in der Programmausführung fort, ohne auf
eine Bestätigung durch den Empfänger zu warten.
Eine Synchronisation von Tasks wird in
PVM
durch Gruppenfunktionen realisiert.
Dabei können einzelne Tasks dynamisch zu Gruppen zusammengefaßt werden.
Die Verwaltung dieser Gruppen erfolgt durch einen Gruppen-Server, der
automatisch bei dem ersten Aufruf einer Gruppenfunktion gestartet wird.
Neben der Synchronisation ( pvm_barrier()) können
Nachrichten an alle Tasks einer Gruppe verschickt ( pvm_bcast())
oder es kann eine globale Funktion (Maximum, Minimum, Summe, Produkt) von
diesen Tasks ermittelt werden ( pvm_reduce()).
Insgesamt kann man sagen, PVM ist ein System, welches einen sehr einfachen Einstieg in die Welt der Parallelverarbeitung ermöglicht und alle zur Parallelprogrammierung notwendigen Funktionalitäten zur Verfügung stellt. Bei der Programmierung kann der Anwender sowohl auf herkömmliche Unix-Tools (z.B. Debugger) zurückgreifen (es ist sogar unter PVM möglich, zu jeder Task einen Debugger-Prozeß zu starten) als auch spezielle Tools zum Monitoring und zur Performance-Analyse der Parallelisierung ( XPVM, ParaGraph ) nutzen.