Friedrich-Alexander-Universität UnivisSuche FAU-Logo
Techn. Fakultät Willkommen am Department Informatik FAU-Logo
Logo I4
Lehrstuhl für Informatik 4
Verteilte Systeme
 
  Vorlesung
  Skript
  Übungen
  Scheine, Prüfungen
Übungen
  Termine, Aufgaben...
  Aufgabe 1
  Aufgabe 2
  Aufgabe 3
  Aufgabe 4
  Aufgabe 5
  Aufgabe 6
Department Informatik  >  Informatik 4  >  Lehre  >  SS 2003  >  VS  >  Übung  >  Aufgabe2

Aufgabe 2: Kommunikations-Basissystem für Fernaufrufe

Aufgabenbeschreibung

Sie haben in der Vorlesung verschiedene Kommunikationsmethoden (synchron oder asynchron, (nicht-) puffernd, (nicht-) blockierend) kennengelernt. In dieser Aufgabe soll ein Basissystem erstellt werden, welches in einem ersten Schritt eine Systemabstraktion bietet mit einer einheitlichen, einfachen Schnittstelle. Die Funktionalität soll auf verschiedene Basismechanismen des Betriebssystem (z.B. Kommunikation mit TCP oder UDP) abgebildet werden. In einem zweiten Schritt soll darauf aufbauend einige der oben erwähnten Kommunikationsvarianten angeboten werden.

Kommunikationsschnittstelle

Das Kommunikationssystem ist stets auf eine objektorientierte Abstraktion mit nachfolgend beschriebenen Elementen abzubilden:
  • Initialisierung:
    Der Konstruktor initialisiert das Kommunikationsobjekt. Er kann ein Adressobjekt als Parameter erhalten, der dann festlegt, auf welcher lokalen Adresse Daten empfangen werden können (z.B. IP-Adresse und Port für UDP). Wird keine lokale Adresse angegeben, wählt (soweit möglich) das System selbst eine. Mit der Methode getLocalAddress kann diese Adresse, unter der das Kommunikationssystem von aussen erreichbar ist, abgefragt werden.
  • Senden von Nachrichten:
    Zum Senden von Nachrichten wird vom System stets eine Methode void send(Addresse *addr, Buffer *buf) angeboten, unabhängig von der Semantik, die dahinter steckt.
    Für den Fall, dass das send zurückkehrt, bevor der Puffer intern vollständig verarbeitet wurde (er darf also nicht verändert werden), ist ein Signalisierungsmechanismus notwendig, um dem Anwender mitzuteilen, dass der Puffer nicht mehr benötigt wird. Hierzu kann durch die Methode set_send_sighand(send_sighand_t fn) ein Signal-Handler installiert werden, der aufgerufen wird, sobald der Puffer nicht mehr benötigt wird.
  • Empfangen von Nachrichten:
    Beim Empfangen von Nachrichten ist eine aktive (wartend) und eine passive (signalisierend) Übergabe möglich.
    Für die aktive Variante ist eine Methode receive(Buffer *buf) bereitzustellen. Diese blockiert, bis eine Nachricht empfangen wurde.
    In der passiven Variante kann der Anwender durch set_receive_sighand(Buffer *buf, recv_sighand_t fn) dem Kommunikationssystem einen Empfangspuffer bereitstellen. Der Signalhandler wird mit Referenz auf den Puffer aufgerufen, sobald Daten empfangen worden sind.
Hierbei gelte:
typedef void(*send_signal_t)(Buffer *buf);
typedef void(*recv_signal_t)(Address *sender, Buffer *buf);

class Buffer {
 public:
    char *buffer;
    int len;
}

Implementierung Teil a: Unterste Systemabstraktion

In dieser Teilaufgaben sollen objektorientierte Abstraktionen für ein einfaches Kommunikationssystem, eine einfache Semaphore und eine Thread-Verwaltung erstellt werden.

Das Kommunikationssystem soll jeweils folgende Funktionalität bereitstellen, jeweils in einer Implementierung für TCP und für UDP:

  • Als Adress-Objekte ist eine Klasse InetAddress zu verwenden, die eine struct sockaddr_in addr; enthält. Eine Initialisierung über Hostname und Portnummer ist vorzusehen.
  • Der Anwender dieser einfachen, untersten Schicht muss damit rechnen, dass der Sendepuffer nach Rückkehr aus dem send-Aufruf vom System noch benötigt wird. Ein Signalisierungsverfahren wie oben beschrieben ist daher zwingend notwendig. (Für TCP und UDP müsste das nicht so sein, aber bei Abbildung auf andere Betriebssystemmechanismen kann dies erforderlich sein!). Die unterste Schicht kann sich darauf verlassen, dass send nicht nebenläufig aufgerufen wird.
  • Zum Empfangen wird das beschriebene Signalisierungsverfahren verwendet.
Ein mögliches Interface könnte also wie folgt aussehen:
class CommunicationSystem {
    public:
        CommunicationSystem(Address *addr);
	Address *getMyAddress();
	void send(Address *dest, Buffer *message);
	void set_send_signaler(send_signal_t fn);
	void set_receive_signaler(Buffer *buffer, recv_signal_t fn);
}

Als zweites ist eine binäre Semaphore bereitzustellen mit folgender Schnittstelle:

class Semaphor{
 public:
    Semaphor();
    void P(void);
    void V(void);
};

Als drittes ist eine einfache Thread-Verwaltung bereitzustellen, die intern direkt auf entsprechende Funktionen der pthread-Bibliothek abgebildet werden können. Der Konstruktor von Thread erzeugt einen neuen Thread, in welchem die Methode run der übergebenen Klasse (welche Runnable implementieren muss) ausführt.
Falls in den folgenden Aufgaben weitere Thread-Methoden benötigt werden, so kann diese Klasse erweitert werden. Im Hinblick auf leichte Portierbarkeit ist die Schnittstelle dabei aber auf eine Minimallösung zu beschränken.

class Runnable {
 public:
   virtual void run() = 0;
}

class Thread {
 private:
  ...
 public:
  Thread(Runnable run);
  void join();
}

Implementierung Teil b:

Es sollen nun drei verschiedene Kommunikationsvarianten auf der Sendeseite implementiert werden, die auf der Schicht aus Teil a aufbauen.
  • Kommunikationsklasse mit synchronem send
    Hier soll eine Methode send implementiert werden, die auf unterer Ebene ein send aus Teil a verwendet. Zusätzlich soll sie mit parallelem Aufrufen aus mehreren Threads zurechtkommen (MT-safe). Synchron heisst, dass sich der send-Aufruf blockiert, bis die untere Schicht das abgeschlossene Versenden signalisiert.
  • Kommunikationsklasse mit asynchronem, puffernden send
    Hier sieht die Kommunikationsklasse intern eine feste Anzahl an Puffern vor. Bei Aufruf von send werden die Daten in den internen Puffer kopiert, und send kehrt sofort zum Aufrufer zurück. Solange keine freien Puffer verfügbar sind, blockiert der send-Aufruf. Der Signalisierungsmechanismus der unteren Schicht wird verwendet, um Puffer freizugeben.
  • Kommunikationsklasse mit asynchronem, nicht-blockierenden send
    Das interne Blockieren bei asynchronem send lässt sich vermeiden, wenn der Pufferspeicher vom Benutzer selbst verwaltet wird. Hier soll also eine Lösung ähnlich der vorangegangenen erstellt werden, bei der aber kein interner Puffer vorgehalten wird, sondern die bei send übergebenen Puffer selbst hierzu verwendet werden. Geeignete Datenstrukturen sind hierzu zu überlegen. Wie bei der Basis-Kommunikation muss hier dem Aufrufer signalisiert werden, wenn der Puffer nicht mehr benötigt wird.

Implementierung Teil c:

Die Implementierung von Teil (a) ist auf Microsoft Windows / Visual Studio zu portieren. Mit der Portierung sollte Teil (b) unverändert zusammenarbeiten.

Die Implementierung dieses Teils wird nicht zwingend bis zum festgelegten Abgabetermin erwartet. Spätere Teilaufgaben werden aber darauf aufbauen, eine Bearbeitung ist daher unerlässlich und sollte zeitnah erfolgen!

Abgabe

bis 13.05.2003/12:00 Uhr

Abgabe mittels /proj/i4vs/pub/abgabe
(Abgabe in 2er oder 3er Gruppen möglich)

  Impressum   Datenschutz Stand: 2003-05-07 11:07   HR, MF