3a. Ergänzungen und Erweiterungen


3a.0 Notation von Kanälen, Senden und Empfangen

Physikalische Leitungen und Übertragungsprotokolle werden durch Kanäle notiert:

channel ch (v1: t1,..., vn: tn).

Dabei ist ch der Name des Kanals. vi bezeichnen (optionale) Variablennamen vom jeweiligen Typ ti. In der Implementierung können die Kanäle sowohl direkte Hardware bezeichnen (z.B. Transputer-Links), aber auch Softwareprotokolle (Pipes und Sockets bei Unix und TCP/IP). Das channel-Statement terminiert, nachdem der Kanal korrekt aufgebaut wurde. Ein Kanal kann unidirektional oder bidirektional sein. Im ersten Fall bewegen sich die Informationen nur in einer Richtung und im zweiten Fall in beiden Richtungen. Ein Kanal kann in mehreren Prozessen bekannt sein, dann können alle diese Prozesse über diesen Kanal miteinander kommunizieren.

Beispielsweise definiert

channel Eingabe (Character);
channel Ausgabe (Integer);

einen Eingabekanal für Zeichen und einen Ausgabekanal für Integer.

Zur Darstellung der eigentlichen Datenübertragung dienen die Konstrukte send und receive. Der Aufruf von send besitzt folgende Syntax:

send ch (e1, ..., en).

Der Parameter ch bezeichnet einen Übertragungskanal und ei stellen Ausdrücke vom Typ ti dar. Die Typen ti entsprechen den Typen der Variablen des Kanals ch. Die Ausdrücke ei werden ausgewertet und das Ergebnis wird auf dem angegebenen Kanal übertragen.

Der receive-Aufruf wird analog zum send-Befehl folgendermaßen definiert:

receive ch (v1, ..., vn).

ch ist wiederum der Name des Übertragungskanals und die vi bezeichnen Variablen vom Typ ti entsprechend der Definition des Kanals ch. Der Befehl liest Daten aus dem Kanal und schreibt sie in die Variablen vi. Sind keine Daten vorhanden, blockiert receive die weitere Ausführung des Programms, bis die erforderlichen Daten eingetroffen sind.

Die Semantik eines send/receive-Paares entspricht somit der Semantik von n Zuweisungen

v1 = value(e1); ...; vn = value(en);

Voraussetzung ist, dass Daten übertragen wurden. Die Verwendung von `value' zeigt an, dass im Empfänger keine Nebeneffekte sichtbar werden.

Während der Aufruf von receive immer blockierend ist, kann der send-Befehl sowohl synchron als auch asynchron arbeiten:

asynchronous send:
Das Programm wird nach dem Einstellen der Daten in einen Sendepuffer ohne weitere Verzögerung fortgesetzt, d.h., send terminiert auch, wenn die Daten erst sehr viel später bei einem Empfänger ankommen.
synchronous send:
Die weitere Programmausführung wird nach dem send so lange blockiert, bis ein entsprechendes receive ausgeführt wurde, d.h., bis die Daten von einem Empfänger wirklich abgenommen worden sind.

Das asynchrone send erfordert einen potenziell unbeschränkt großen Puffer, während das synchrone send nur einen Puffer fester Größe benötigt. Die Verwendung des synchronen send ist schwieriger, da es sehr genau auf die zeitlich richtige Reihenfolge aller send- und receive-Operationen ankommt. Das synchrone send wird daher fast nur mit so genannten `selected receives' verwendet, bei denen mehrere receives zusammengefasst werden und dasjenige receive zur Ausführung ausgewählt wird, für das ein send wartet.

Für diesen Fall ist auch die Funktion

public boolean empty(ch)

wichtig; sie testet, ob Daten in einem Kanal vorhanden sind. Bei der Verwendung von empty() ist aber Vorsicht geboten, da die Funktion nur Momentaufnahmen vom Zustand des Kanals liefert. So können beispielsweise, kurz nachdem die Funktion den Wert true zurückgeliefert hat, Daten eintreffen. Umgekehrt können die Daten, kurz nachdem die Funktion false zurückgeliefert hat, bereits durch einen anderen Prozess abgeholt worden sein (falls mehrere Prozesse von diesem Kanal Daten empfangen).

In MPI (Message Passing Interface) gibt es eine weitere Variante des send-Konstrukts (zu MPI siehe Kapitel 8). In Performance-Untersuchungen der MPI Software wurde festgestellt, dass ein großer Teil der Rechenzeit von send/receive für das mehrmalige Kopieren der Daten verwendet wird. So werden die Daten vom Benutzerprozess in das Betriebssystem und dann weiter in die Kommunikationshardware kopiert. Um diesen Effekt zu minimieren, wird ein so genanntes `zero-copy'-send angeboten. Dabei muss im Programm allerdings sichergestellt werden, dass während der Zeit, in der die Daten noch nicht den Rechner bzw. den Prozess verlassen haben, keine Änderungen an den Daten vorgenommen werden dürfen. Mit einem blockierenden Funktionsaufruf ``wait send buffer empty'' muss bei Bedarf explizit auf die Wiederverwendbarkeit des Sendepuffers gewartet werden.


© Universität Mannheim, Rechenzentrum, 2000-2002.

Last modified: Tue Jul 16 22:07:58 MEST 2002