Interprozesskommunikation

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche

Unter Interprozesskommunikation (englisch inter-process communication, IPC) versteht man Methoden zum Informationsaustausch, informatisch gesprochen Datenübertragung, von nebenläufigen Prozessen oder Threads. Im engeren Sinne versteht man unter IPC die Kommunikation zwischen Prozessen auf demselben Computer, deren Speicherbereiche aber strikt voneinander getrennt sind (Speicherschutz). Im weiteren Sinne bezeichnet IPC aber jeden Datenaustausch in Verteilten Systemen, angefangen bei Threads, die sich ein Laufzeitsystem teilen, bis hin zu Programmen, die auf unterschiedlichen Rechnern laufen und über ein Netzwerk kommunizieren.

Für die Kommunikation ist dabei eine geeignete Prozesssynchronisation notwendig, insbesondere wenn verschiedene Prozesse potentiell gleichzeitig auf dieselben Betriebsmittel zugreifen können. Dabei sind neben Dateien, Peripheriegeräten, etc auch andere Prozesse, mit denen kommuniziert werden soll, als Ressourcen zu verstehen. Dies wird je nach Betriebssystem beziehungsweise Laufzeitumgebung und Art der Ressource durch Locks, Semaphore, Monitore oder Ähnliches realisiert. Klassische Probleme der Interprozesskommunikation sind die speisenden Philosophen oder das Erzeuger-Verbraucher-Problem.

Für die Interprozesskommunikation stehen je nach Betriebssystem beziehungsweise Laufzeitumgebung verschiedene Methoden zur Verfügung:

Datenströme[Bearbeiten]

Datenströme sind Kommunikationskanäle, die Dateneinheiten (einzelne Bytes oder auch komplexe Objekte) sequentiell von einem Prozess zu einem anderen weiterreichen. Der eine Prozess (der Produzent) schreibt Daten in den Datenstrom, der andere (der Konsument) liest die Daten aus dem Strom, wobei er auf neue Daten wartet, bis der Produzent diese liefert oder den Strom schließt. Meistens besitzt der Strom einen internen FIFO-Puffer, so dass der Produzent auch dann schreiben kann, wenn der Konsument die Daten gerade nicht (oder nicht schnell genug) verarbeitet. Erst wenn dieser Puffer voll ist, muss der Produzent beim Schreiben solange warten, bis der Konsument wieder Daten entnommen hat (oder die Kommunikation bricht in einem solchen Fall mit einem Fehler ab).

Die verbreitetste Variante dieser Art der Kommunikation sind so genannte Pipes, wie sie durch verschiedene Unix-Derivate populär wurden und heute von den meisten Betriebssystemen unterstützt werden. Pipes übertragen sequentiell einzelne Bytes. Jedem Prozess sind immer zwei Datenströme zugeordnet, einer für die Ausgabe und einer für die Eingabe. Ein Ausgabestrom eines Prozesses kann dann direkt mit dem Eingabestrom eines anderen Prozesses verbunden werden. Wird keine explizite Zuordnung vorgenommen, so werden die Datenströme direkt für die Kommunikation mit dem Benutzer verwendet (über ein Terminal), das heißt der Eingabestrom „liest“ von der Tastatur und die Ausgabeströme schreiben auf den Bildschirm.

Eine Erweiterung dieses Konzeptes stellen Named Pipes dar: Diese sind nicht einem Prozess fest zugeordnet, sondern können genau wie Dateien geschrieben und gelesen werden. Sie existieren als Objekte im Dateisystem mit einem Namen, und bieten so eine sehr flexible Schnittstelle für die Kommunikation mit einem Prozess.

Auch verbindungsorientierte Netzwerkprotokolle (besonders TCP) verhalten sich wie Datenströme und können zur Interprozesskommunikation benutzt werden (man spricht auch von Sockets). Allerdings haben sie den Vorteil, dass man über sie auch mit Prozessen kommunizieren kann, die sich auf einem anderen Computer befinden.

POSIX-kompatible Betriebssysteme machen keinen Unterschied zwischen diesen drei Arten von Datenströmen: sie werden alle über einen so genannten File Descriptor angesprochen. So müssen die Funktionen eines Programms nicht wissen, ob sie über eine Pipe, eine Named Pipe oder einen Socket kommunizieren.

Message Queues dagegen funktionieren zwar auch als Datenströme, haben aber als Grundeinheit beliebig komplexe Nachrichten statt einzelner Bytes. Sie sind wesentlich weniger weit verbreitet.

Ein Vorgänger der Datenströme, der heute nur noch in Spezialfällen verwendet wird, ist das Rendezvous, das man sich als Datenstrom ganz ohne internen Puffer vorstellen kann. Das bedeutet also, dass der Produzent nur schreiben kann, wenn der Konsument gerade auf Daten wartet, und umgekehrt der Konsument immer warten muss, bis der Produzent wieder Daten liefert. Das führt dazu, dass sich die beiden Prozesse für die Übergabe der Daten „treffen“ müssen, daher der Name.

Ereignisse[Bearbeiten]

Manche Betriebssysteme erlauben Prozessen auf Ereignisse in anderen Prozessen zu reagieren – solche Ereignisse können auch als Nachrichten verstanden werden, die ein Prozess einem anderen sendet (siehe Nachrichtenaustausch). Dazu muss der Prozess ein Unterprogramm als Behandlungsroutine für dieses Ereignis registrieren, so dass es immer ausgeführt wird, wenn das Ereignis eintritt. Dieses Konzept weicht von der klassischen Methode ab, Programme als einen linearen Ablauf von Befehlen umzusetzen – eine klare Programmfolge gibt es dann nur innerhalb der Behandlungsroutinen (siehe Inversion of Control).

Ein Beispiel für diese Art der Kommunikation, die auch für den Benutzer sichtbar ist, ist die Verwendung der Zwischenablage einer grafischen Benutzeroberfläche: beim Einfügen erhält der Prozess das Signal, Daten aus einem Puffer zu übernehmen und in ein Dokument einzufügen.

Die älteste Variante dieses Konzeptes sind Software Interrupts: diese werden direkt vom Prozessor aufgerufen, wenn eine Unterbrechungsanforderung ausgelöst wird, entweder von einer Hardwarekomponente oder von einem anderen Prozess. Neuere Betriebssysteme bieten eine flexiblere Schnittstelle zum Registrieren von Ereignisbehandlungsroutinen, zum Beispiel DDE bei Windows oder Apple Events von Apple.

Elemente zur Prozesssynchronisation können selbst auch als eine Art Ereignisbehandlung verstanden werden: Dabei wartet ein Prozess auf ein Ereignis (nämlich der Freigabe eines Locks oder eines Semaphors), das von einem anderen Prozess ausgelöst wird. Manche Betriebssysteme bieten Mechanismen an, um dies prozessübergreifend zu erlauben. Besonders für den Zugriff auf Dateien stehen meist solche Mechanismen zur Verfügung, die auch zur Synchronisation von Prozessen verwendet werden können, ohne dass der Inhalt der Datei tatsächlich benötigt wird.

Eine besondere Art von Ereignissen sind die so genannten Signale, die der Steuerung des Prozesses selbst dienen; vor allem dazu, den Prozess abzubrechen. Für solche Signale muss sich ein Prozess nicht explizit anmelden, das Betriebssystem sorgt bei vielen Signalen dafür, dass sich der Prozess entsprechend verhält.

Funktionsaufrufe[Bearbeiten]

Die aus Sicht des Programmierers einfachste Art auf einen anderen Prozess zuzugreifen ist es, auf einzelne Funktionen des Prozesses so Zugriff zu haben, als seien es Funktionen innerhalb des Prozesses – also ein Verfahren, das es für den Programmierer transparent macht, wo sich die Implementierung einer Funktion befindet – im eigenen Prozess oder in einem anderen, der sich möglicherweise sogar auf einem anderen Computer befinden kann.

Für viele Programmiersprachen gibt es solche Konzepte, allerdings sind sie meist nicht Teil des Laufzeitsystems oder des Betriebssystems sondern werden von zusätzlichen Bibliotheken und Dienstprogrammen zur Verfügung gestellt (man spricht von Middleware). Die verbreitetsten solcher Systeme sind der Remote Procedure Call und die neuere Remote Method Invocation (beide von Sun Microsystems), DCOM (von Microsoft), D-Bus (von freedesktop.org), DCOP (von KDE, inzwischen veraltet und durch D-Bus abgelöst) und CORBA.

Funktionsaufrufe sind im Allgemeinen synchron, sie blockieren, bis der andere Prozess ein Ergebnis liefert. Asynchrone Funktionsaufrufe werden häufig über Futures realisiert, wie sie zum Beispiel CORBA mit der Asynchronous Method Invocation anbietet – dies ist aber meist nicht ohne explizite Umsetzung durch den Programmierer möglich. Manche Programmiersprachen integrieren dieses Konzept aber direkt in die Sprache selbst, um so eine transparente Kommunikation zwischen Threads zu erlauben.

Shared Memory[Bearbeiten]

Eine weitere Methode zur Kommunikation zwischen Programmen ist gemeinsamer Speicher. Dabei werden Bereiche des Arbeitsspeichers für den Zugriff durch mehrere Prozesse reserviert, wobei das Lesen und Schreiben durch einen Mutex-Mechanismus geregelt wird. Der Vorteil ist eine schnelle Übertragung von großen Datenmengen, allerdings muss durch einen zusätzlichen Mechanismus dafür gesorgt werden, dass der Empfängerprozess auch erfährt, wann Daten für ihn vorliegen. Das geschieht häufig durch eine Art von Ereignisbehandlung bzw. durch prozessübergreifende Semaphore oder Locks. Bei Zugriffen auf gemeinsamen Speicher kommen Algorithmen zum Erzeuger-Verbraucher-Problem und zum Leser-Schreiber-Problem zum Einsatz. Siehe auch: Deadlock.

Siehe auch[Bearbeiten]