|
Unbenannte Pipes werden von vielen Betriebssystemen zur Verfügung gestellt, da es sich um eine seit langem bekannte Form der Interprozeßkommunikation handelt. Es gibt zwei Arten von Pipes
- unidirektionale
- bidirektionale
Bei unidirektionalen Pipes findet der Datenfluß in einer Richtung statt. Ein Prozeß schreibt Daten in die Pipe, der andere liest diese Daten aus der Pipe. Bidirektionale Pipes kann man sich auch als zwei unidirektionale Pipes vorstellen.
| |
|
Beim Schreiben und Lesen blockieren die Prozesse, wenn die Pipe gerade voll bzw. leer ist. Schreibt ein Prozeß in eine Pipe, so kehrt dieser erst wieder aus dem write-Funktionsaufruf zurück, wenn die Daten geschrieben werden konnten. Ist die Pipe gerade nicht in der Lage, die Daten aufzunehmen, so wird der schreibende Prozeß angehalten, bis der lesende Prozeß genügend Daten gelesen hat, damit die Pipe die Daten des Schreibers aufnehmen kann. Liest ein Prozeß Daten aus einer Pipe, so kehrt der read-Funktionsaufruf erst zurück, wenn genügend Daten in der Pipe vorhanden sind. Unter Umständen wird gewartet, bis ein schreibender Prozeß entsprechend viele Daten in die Pipe geschrieben hat. Die Daten werden nach dem FIFO-Prinzip verwaltet. Die Reihenfolge der Daten bleibt also erhalten.
| Blockieren der Prozesse |
|
Diese Art der Interprozeßkommunikation bietet Kopplung und Entkopplung zugleich. Jenseits der Größe der Pipes sind die Prozesse gekoppelt (warten aufeinander), innerhalb der Pipegröße sind sie entkoppelt (arbeiten unabhängig voneinander). Die Pipe wird von einem Prozeß erzeugt, der andere Prozeß greift über ein Handle auf die bereits geöffnete Pipe zu. Dies hat zur Konsequenz, daß beide Prozesse die Handles für Lesen und Schreiben kennen müssen, und daß die Handles in diesen Prozessen mit der Pipe verknüpft sein müssen.
| Kopplung und Entkopplung |
|
Im Gegensatz hierzu wird eine benannte Pipe über einen gemeinsam bekannten Namen geöffnet. Ein Name kann zur Übersetzungszeit definiert werden, ein Handle erhält man aber erst zur Laufzeit vom Betriebssystem. Bei den unbenannten Pipes muß ein Übergabemechanismus für ein Handle zwischen den beiden Prozessen vorhanden sein, und die offenen Handles des einen Prozesses müssen dem anderen vererbt werden können.
| Benannte Pipe |
|
Die einfachste Art, diese Handles zu übergeben, ist in Unix gegeben. Ein Prozeß forkt sich nach dem Erzeugen der Pipe. Beim fork-Funktionsaufruf werden die offenen Handles dem Kindprozeß vererbt. Da der Quellcode von Vater- und Kindprozeß in derselben Datei vorhanden ist, können beide Prozesse direkt auf die Handles zugreifen.
| Funktionsaufruf fork |
|
OS/2 und Win32 bieten keinen fork-Funktionsaufruf. Hier müssen die API-Aufrufe DosExecPgm bzw. CreateProcess verwendet werden. Diese Aufrufe vererben zwar die gültigen Handles an den Kindprozeß, aber die Quelltexte der beiden Prozesse sind unterschiedlich. Es muß zusätzlich ein Weg gefunden werden, das Handle dem Kindprozeß zu übergeben. Der einfachste Weg ist die Parameterübergabe in der Kommandozeile.
OS/2 Funktionen
Alle OS/2-API-Basisfunktionen sind vom Typ APIRET, der dem Typ unsigned long entspricht. Alle Funktionen vom Typ APIRET liefern einen Fehlercode zurück. NO_ERROR bedeutet kein Fehler und hat den Wert 0.
| Funktionsaufrufe DosExecPgm CreateProcess |
APIRET DosCreatePipe(PHFILE phfRead,
PHFILE phfWrite,
ULONG cb);
DosCreatePipe erzeugt eine bidirektionale Pipe. Die ersten beiden Parameter sind Zeiger auf Dateihandles (HFILE). Über diese Zeiger werden die Handles für die Lese- und Schreibzugriffe der Pipe zurückgegeben. Der letzte Parameter gibt die Größe der Pipe in Bytes an.
| Funktion DosCreatePipe |
APIRET DosClose(HFILE hFile);
DosClose schließt die Pipe. Dieser Funktion muß als Parameter ein Handle übergeben werden. Sie muß zum korrekten Beenden der Pipe zweimal aufgerufen werden, einmal für das Lesehandle und einmal für das Schreibhandle.
| Funktion DosClose |
APIRET DosRead(HFILE hFile,
PVOID pBuffer,
ULONG cbRead,
PULONG pcbActual);
DosRead liest aus der Pipe. Der erste Parameter ist das Lesehandle der Pipe, der zweite Parameter der Zeiger auf den Puffer, der dritte Parameter bedeutet die Anzahl der Bytes, die gelesen werden sollen. Der vierte Parameter ist ein Zeiger auf einen unsigned-long-Wert. Verlief das Lesen erfolgreich, so steht nach dem Funktionsaufruf in diesem Wert die Anzahl der tatsächlich gelesenen Bytes.
| Funktion DosRead |
APIRET DosWrite(HFILE hFile,
PVOID pBuffer,
ULONG cbWrite,
PULONG pcbActual);
DosWrite ist das Gegenstück zu DosRead. Selbstverständlich muß der Funktion als erster Parameter das Schreibhandle der Pipe übergeben werden.
Linux-Funktionen
| Funktion DosWrite |
int pipe(int filedes[2]);
pipe erzeugt eine unidirektionale Pipe. Die Funktion benötigt als Parameter einen Zeiger auf ein Array, das zwei Deskriptoren enthält. In diesem Array werden nach einem erfolgreichen Funktionsaufruf die Deskriptoren für das Lesen (fildes[0]) und das Schreiben (fildes[1]) abgelegt. Die Funktion liefert das Ergebnis -1, wenn die Pipe nicht angelegt werden konnte. Ist die Pipe korrekt erzeugt, so ist das Ergebnis 0.
| Funktion pipe |
int close(int filedes);
int read (int filedes,
void *buffer,
int laenge);
int write(int filedes,
void *buffer,
int laenge);
close, read und write sind identisch mit den Funktionen für die Dateibearbeitung. Unix unterscheidet nach außen hin nicht zwischen unterschiedlichen Handles, seien sie für Dateien, für Pipes oder für Sockets.
Win32-Funktionen
Win32S stellt die nachfolgenden Funktionen bereit:
BOOL CreatePipe(
PHANDLE hReadPipe,
PHANDLE hWritePipe,
LPSECURITY_ATTRIBUTES lpPipeAttributes,
DWORD nSize);
BOOL CloseHandle(HANDLE hObject);
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
PDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped);
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);
| Funktionen close read write |
|
Wegen der Ähnlichkeit der Funktionalitäten kann für OS/2, Linux und Win32 ohne Probleme eine C++-Klasse für Pipes geschaffen werden. Wohlgemerkt, die drei Implementierungen sind sehr wohl unterschiedlich. Dies stellt jedoch kein Problem für die Verwendbarkeit unter den verschiedenen Betriebssystemen dar. Das Programm muß nur mit unterschiedlichen Pipemodulen gelinkt werden. Wichtig für die Portierbarkeit ist, daß die public-Schnittstelle der Klasse gleich ist.
| Klasse für OS/2, Linux und Win32 |
|
Das für die Anwendung der Klasse interessante public Interface beinhaltet zwei Konstruktoren, den Destruktor, die Funktionen für das Lesen und das Schreiben, eine Funktion, die den Status nach dem Erzeugen zurückgibt, und jeweils eine Funktion für die Rückgabe des Lese- bzw. des Schreibdeskriptors.
| public Interface |
|
Der Konstruktor mit dem optionalen int-Parameter erzeugt eine Pipe. Der Parameter gibt die Größe der Pipe an. Sie ist mit 4096 Bytes voreingestellt. Dieser Parameter ist nur unter OS/2 und Win32 funktionsfähig. Unter Linux zeigt er keine Auswirkungen, da dort die Größe der Pipe nicht eingestellt werden kann.
| Größe der Pipe |
|
Der andere Konstruktor dient zum Ankoppeln an eine bereits erzeugte Pipe. Ihm müssen als Parameter die Deskriptoren für den Lese- und den Schreibzugriff übergeben werden, er erzeugt keine neue Pipe. Dieser Konstruktor kann Probleme bereiten. Da auf eine unbenannte Pipe im nicht erzeugenden Prozeß nur über die Deskriptoren zugegriffen werden kann, müssen diese Deskriptoren außerhalb der Klasse bekannt sein. C++ bietet leider nicht die Möglichkeit, Klassen an andere Prozesse zu übergeben. Die Unix Funktion fork, die aus einem Prozeß einen zweiten erzeugt, löst dieses Problem.
| Ankopplung an eine Pipe |
|
Da der Eltern- und der Kindprozeß in derselben Quellcodedatei enthalten sind, wird die Pipe Klasse an den Kindprozeß übertragen. Der Datentyp der Deskriptoren ist deshalb in der Klassenschnittstelle enthalten und kann nicht innerhalb der Klasse verborgen werden. Bei der Portierung der Klasse auf ein anderes Betriebssystem kann es hier zu Änderungen kommen, oder es werden unschöne Kunstgriffe nötig. In dieser Version sind die Deskriptoren int-Werte, was für OS/2, Linux und Win32 zumindest ohne Probleme funktioniert.
| Klassenschnittstelle |
|
getStatus liefert den Status der Pipe zurück. Diese Funktion braucht nur nach dem Erzeugen der Pipe aufgerufen zu werden, also nach dem Konstruktor mit dem Parameter für die Größe der Pipe. Sicher hätte man hier auch mit Ausnahmen arbeiten können. Da aber die Zeit für Ausnahmen noch nicht gekommen scheint (es gibt noch Compiler, die keine Ausnahmen unterstützen), wurde der Weg über den Status gewählt, zumal es nur eine Stelle gibt, die den getStatus-Funktionsaufruf notwendig macht.
| Funktion getStatus |
|
Die restlichen beiden Funktionen des public-Bereichs sind ebenso wegen der Übergabe der Deskriptoren an den Kindprozeß notwendig. Sie liefern die Deskriptoren für den Lese- bzw. den Schreibzugriff auf die Pipe.
| public-Bereich |
|
Die Funktionen im protected-Bereich der Klasse sind zwei weitere Konstruktoren und der Zuweisungsoperator. Da es nicht sinnvoll erscheint, Objekte der Pipeklasse zu duplizieren, sind diese Funktionen nicht implementiert, so daß beim Versuch der Duplizierung einer Pipe sich der Linker weigert, das Programm zu erzeugen. Sollte es doch einen sinnvollen Einsatzbereich für eine oder mehrere der drei Funktionen geben, so kann die Pipeklasse abgeleitet und in der begleiteten Klasse die Funktionen implementiert werden.
| protected-Bereich |
|
Im private-Bereich der Klasse sind das Flag, das anzeigt, ob die Pipe ordentlich erzeugt werden konnte, und die beiden Deskriptoren für das Lesen und das Schreiben.
Implementierung für OS/2
Die Implementierung für OS/2 befindet sich in der Datei PIPE.CPP. Auch die Implementationen für Linux und Win32 sind in PIPE.CPP-Dateien, aber in anderen Unterverzeichnissen. Auf eine bedingte Programmierung (alle Quellcodes in einer Datei) wurde verzichtet, da die Übersichtlichkeit darunter oft erheblich leidet. Eine getrennte Haltung der Quellcodes birgt allerdings die Gefahr, daß bei Änderungen vergessen wird, diese auch in den anderen Dateien durchzuführen.
| private-Bereich |
|
Der Konstruktor ohne Parameter erzeugt die Pipe mit dem OS/2-Systemaufruf DosCreatePipe. Konnte die Pipe erzeugt werden, so wird das Flag, das den Zustand der Pipe angibt, mit 1 initialisiert. Der Aufruf DosCreatePipe initialisiert beide Deskriptoren direkt. Im Fehlerfall wird das Flag auf 0 gesetzt, und die beiden Deskriptoren werden mit -1 initialisiert.
| Konstruktor |
|
Der Konstruktor mit den beiden Parametern für die Deskriptoren für das Lesen und das Schreiben initialisiert nur die Datenelemente der Klasse.
| Konstruktor |
|
Der Destruktor schließt die beiden Kanäle der Pipe, falls diese ordnungsgemäß erzeugt war.
| Destruktor |
|
Die Memberfunktion read der Klasse Pipe liest eine als Parameter übergebene Anzahl Bytes in den Puffer, auf den der Zeiger des ersten Parameters zeigt. Das Lesen aus der Pipe übernimmt die Funktion DosRead. Die Funktion gibt die Anzahl der gelesenen Bytes zurück. Trat ein Fehler auf, so ist das Funktionsergebnis -1.
| Memberfunktion read |
|
Die Funktion write schreibt die als Parameter übergebene Anzahl Bytes aus dem Puffer, auf den der erste Parameter zeigt, in die Pipe. Trat ein Fehler beim Schreiben mit der Funktion DosWrite auf, so liefert die Funktion das Ergebnis -1, ansonsten die Anzahl der geschriebenen Bytes.
Implementierung für Linux
Die Implementierung der Klasse Pipe für Linux unterscheidet sich erwartungsgemäß von der Implementierung für OS/2. Wie schon erwähnt, befindet sich der Quellcode ebenfalls in der Datei PIPE.CPP, allerdings im Unterverzeichnis LINUX.
| Memberfunktion write |
|
Der Konstruktor ohne Parameter erzeugt die Pipe mit der Linux-Funktion pipe. Konnte die Pipe erzeugt werden, so werden die Deskriptoren den zugehörigen Klassenelementen zugewiesen und das ok-Flag auf 1 gesetzt. Im Fehlerfall werden natürlich auch bei dieser Implementierung die Deskriptoren auf -1 und das ok Flag auf 0 gesetzt.
Die nachfolgenden Funktionen sind identisch mit den Implementierungen der Version für OS/2.
Pipe::Pipe(int lesedesc, int schreibdescr)
Pipe::~Pipe(void)
| Konstruktor |
|
Die Funktionen Pipe::read und Pipe::write unterscheiden sich in der Version für Linux. Die Funktionen rufen ihrerseits die beiden Betriebssystemfunktionen read und write auf. Der Aufruf kann aber nicht einfach read(...) oder write(...) lauten, da sonst die Pipe::read- bzw. Pipe::write-Funktionen sich selbst rekursiv aufrufen würden. Die Schreibweise für die globalen Funktionen lautet ::read(...) und ::write(...).
Implementierung für Win32
Die Implementierung der Klasse Pipe für Win32 unterscheidet sich erwartungsgemäß von den Implementierungen für OS/2 und Linux. Wie schon erwähnt, befindet sich der Quellcode ebenfalls in der Datei PIPE.CPP, allerdings im Unterverzeichnis NT.
| Funktionen read und write |
|
Der Konstruktor mit dem Integerwert für die Größenangabe legt eine unbenannte Pipe an. Das Win32-API bietet hierzu die Funktion CreatePipe. Die Funktion liefert die beiden Handles für den Lese- und den Schreibzugriff der Pipe. Der letzte Parameter bestimmt die Größe der Pipe. Der dritte Parameter ist der Tribut, der für die Sicherheit von Win32 zu zollen ist. Ein Zeiger auf die Sicherheitsattribute muß übergeben werden. Dieser Zeiger kann auch NULL sein, was bewirkt, daß die Standardsicherheitsattribute vom System verwendet werden. In diesem Fall ist es mit der NULL nicht getan, da die Handles der Pipe an den Kindprozeß vererbt werden sollen. Zwar kann man der Funktion CreateProcess, die den Kindprozeß erzeugt, mitteilen, daß alle Handles vererbt werden sollen, dies funktioniert aber nur, wenn die Pipe mit entsprechenden Sicherheitsattributen erzeugt wurde.
| Konstruktor |
|
Die Struktur vom Typ SECURITY_ATTRIBUTES enthält drei Werte:
- nLength
- lpSecurityDescriptor
- bInheritHandle
Der erste Parameter gibt die Länge der Struktur selbst an, der zweite Parameter ist ein Zeiger auf die Sicherheitsbeschreibung, der aber in diesem Fall mit NULL initialisiert werden kann. Der dritte Parameter legt fest, daß beide Handles zum Lesen und Schreiben vererbt werden können. Konnte die Pipe nicht erzeugt werden, so werden beide Deskriptoren der Klasse Pipe mit -1 initialisiert und das Flag ok auf 0 gesetzt.
| Sicherheitsattribute |
|
Der Konstruktor mit den beiden Integerparametern dient dem Ankoppeln an eine existierende Pipe. Diese Pipe muß natürlich vom erzeugenden Prozeß vererbt worden sein, damit der Prozeß auf diese Pipe zugreifen kann.
| Konstruktor |
|
Der Destruktor der Klasse Pipe schließt beide Kanäle für das Lesen und das Schreiben, falls sie gültig waren. Im WIN32-API erledigt dies die Funktion CloseHandle. Sie ist für alle Objekte, die mit Handles verwaltet werden, zuständig. Die Funktion erkennt anhand des Typs des Handle, um welche Art von Objekt es sich handelt.
| Destruktor |
|
Mit read werden Bytes aus der Pipe gelesen. Das Lesen aus einer Pipe im Win32-API übernimmt die Funktion ReadFile. Kann nur eine geringere Anzahl Bytes gelesen werden, als im übergebenen Parameter spezifiziert ist, so werden diese Bytes gelesen und die Anzahl als Funktionsergebnis zurückgegeben. Wird die Funktion ReadFile und somit auch die Memberfunktion read der Pipe-Klasse aufgerufen, wenn keine Bytes in der Pipe vorhanden sind, so blockiert die Funktion, bis der schreibende Prozeß mindestens ein Zeichen in die Pipe geschrieben hat. Liegt ein Fehler beim Lesen vor, so liefert die Funktion als Ergebnis -1.
| Memberfunktion read |
|
Die Memberfunktion write schreibt Bytes in die Pipe. Sie liefert die Anzahl der wirklich geschriebenen Bytes als Funktionsergebnis zurück. Diese Anzahl kann kleiner sein als die Anzahl der zu schreibenden Zeichen, die im Parameter anzahl übergeben werden muß. Sie ist kleiner, wenn die Pipe während des Schreibens voll wird. Ist die Pipe schon vor dem Aufruf voll, so blockiert die Funktion, bis der lesende Prozeß mindestens ein Zeichen gelesen hat. Das Schreiben in die Pipe erledigt die Win32-API-Funktion WriteFile.
Beispielanwendung für OS/2
Beim Beispiel für OS/2 sind zwei unterschiedliche Prozesse beteiligt. Der eine produziert Daten und schreibt diese Daten in die Pipe. Er befindet sich in der Datei PRODUCE.CPP.
Die Pipe wird bereits im Konstruktor erzeugt. Durch den Aufruf der Memberfunktion getStatus wird sichergestellt, daß die Erzeugung ohne Fehler verlief. Das Aufbereiten der Kommandozeile für den mit der Funktion DosExecPgm zu startenden Kindprozeß ist etwas gewöhnungsbedürftig.
| Memberfunktion write |
|
Das Array kommandopuffer beinhaltet das Kommando. In dieses Kommando werden mit den beiden sprintf-Anweisungen die Deskriptoren eingesetzt, wobei der Stringdelimiter der ersten sprintf-Anweisung wieder aus dem Kommando gelöscht werden muß. Die Kommandozeile muß nach dem Prozeßnamen (argv[0]) einen 0-Delimiter enthalten. Die gesamte Kommandozeile muß mit zwei Stringdelimitern abgeschlossen werden. Diese Form des Strings kann mit den C-Funktionen strcpy und strcat nicht erzeugt werden.
| Kommandozeile |
|
Der Funktionsaufruf DosExecPgm vererbt unter anderem alle Pipedeskriptoren. Allerdings müssen sie dem Kindprozeß mitgeteilt werden, was über die Kommandozeile erfolgt. Nachdem der Kindprozeß gestartet ist, werden alle Großbuchstaben des Alphabets in die Pipe geschrieben. In diesem Fall braucht nicht auf den Kindprozeß gewartet zu werden, diese Aufgabe erledigt die Pipe automatisch. Erst wenn die Pipe leer ist, kann sie geschlossen werden. Bis die Pipe leer ist, wird der Prozeß im DosClose-Funktionsaufruf, der sich im Destruktor der Klasse Pipe befindet, blockiert.
| DosExecPgm |
|
Der Prozeß, der die Daten aus der Pipe liest und am Bildschirm anzeigt, ist in der Datei CONSUME.CPP. In der Kürze liegt die Würze, könnte man sagen. Auf den ersten Blick fällt nicht auf, daß an diesem Programm eine Pipe beteiligt ist. Die beiden Deskriptoren der Pipe erhält das Programm über die Kommandozeile. Die Ankopplung an die vom Elternprozeß erzeugte Pipe erfolgt im Konstruktor. Es werden alle Großbuchstaben aus der Pipe gelesen und am Bildschirm angezeigt. Im Destruktor des Objekts pipe wird die inzwischen leere Pipe geschlossen. Nach diesem Schließen kann auch der Elternprozeß die Pipe schließen, und beide Prozesse können sich beenden.
Anwendung für Linux
Verglichen mit den OS/2 Programmen für die Anwendung der Pipe, sieht das Linux Programm unvollständig aus. Es wird nur eine Quelldatei benötigt, die PIPTEST.CPP heißt.
| Kindprozeß |
|
Auch hier wird die Pipe im Konstruktor erzeugt. Nach dem Abprüfen mit dem Funktionsaufruf getStatus, ob die Pipe angelegt wurde, kann mit dem fork-Aufruf der Kindprozeß erzeugt werden. Ist die Prozeß-ID (pid) ungleich 0, so befindet man sich nach dem fork Aufruf im Elternprozeß. Die pid ist die Prozessid des Kindprozeßes. Ist die pid 0, so befindet man sich im Kindprozeß. Der Elternprozeß schreibt alle Großbuchstaben in die Pipe, der Kindprozeß liest diese aus der Pipe heraus und zeigt sie am Bildschirm an. Durch den fork-Aufruf kann man sich die Übergabe der Pipedeskriptoren an den Kindprozeß sparen. Allerdings ist diese Art der Programmierung etwas gewöhnungsbedürftig, da Eltern- und Kindprozeß im selben Quelltext enthalten sind.
Anwendung für Win32
Das Win32-Programm, das eine umbenannte Pipe anlegt, einen Kindprozeß erzeugt und Bytes in die Pipe schreibt, ist in der Datei CONSUME.CPP (in Unterverzeichnis NT).
Das Anlegen der Pipe ist der geringste Aufwand. Die Deklaraton eines Objekts genügt. Konnte die Pipe korrekt angelegt werden, so wird der Kindprozeß gestartet. Diese Aufgabe ist vom Betriebssystem abhängig und erfordert das Aufbereiten der Kommandozeile und das Initialisieren einer Struktur vom Typ STARTUPINFO. Die Kommandozeile enthält den Namen des zu erzeugenden Prozesses und die Handles für Lese- und Schreibdeskriptoren der unbenannten Pipe.
| Prozeß-ID |
|
Die Funktion CreateProcess erzeugt den Kindprozeß und liefert in der Struktur vom Typ PROCESS_INFORMATION das Handle des Kindprozesses, das benötigt wird, um auf sein Programmende zu warten.
| CreateProcess |
|
Dieses Warten übernimmt die Funktion WaitForSingleObject. Vor dem Warten auf den Kindprozeß schreibt das Programm alle Großbuchstaben des Alphabets in die unbenannte Pipe.
| WaitForSingleObject |
|
Das Programm, das die unbenannte Pipe ausliest und die Zeichen anzeigt, ist in der Datei CONSUME.CPP. Ist die korrekte Anzahl Parameter in der Kommandozeile vorhanden, so wird an die unbenannte Pipe angekoppelt. Diese Ankopplung erledigt der Konstruktor mit den beiden Integerparametern. Diese Parameter sind die Deskriptoren für das Lesen und das Schreiben. Sie müssen dem Programm in der Kommandozeile übergeben werden. Die Pipe wird ausgelesen, und die Zeichen werden am Bildschirm angezeigt. Zum Beenden des Programms muß die Entertaste betätigt werden.
C++ macht´s möglich: Eine Klasse für unbenannte Pipes für drei weitverbreitete Multitasking-Betriebssysteme. Sicher ist eine Lösung mit einer C-Bibliothek auch zu realisieren, bei der Lösung mit der Klassenbibliothek können allerdings spezialisierte Pipes durch eine einfache Ableitung erzeugt werden. Diese spezialisierten Pipes sind zum Beispiel Filter, die einen Datenstrom zwischen zwei Prozessen manipulieren. Ein oft benötigter Filter konvertiert Ascii-Dateien zwischen OS/2 oder Windows NT und Unix. Hierbei muß das Zeilenendezeichen Carriage Return plus Line Feed in Line Feed und umgekehrt gewandelt werden.
| Kindprozeß
|