Mehrere Pipes in C

Waqar Aslam 12 Oktober 2023
  1. Übersicht über Pipes in C
  2. Implementieren Sie mehrere Pipes in C
Mehrere Pipes in C

Dieser Artikel demonstriert die Implementierung mehrerer Pipes in C.

Übersicht über Pipes in C

Eine Pipe ist eine abstrakte Darstellung einer Verbindung zwischen zwei verschiedenen Prozessen, bei der die von einem Prozess zurückgegebene Standardausgabe zur Standardeingabe des anderen wird. Pipes sind eine wichtige Komponente des UNIX-Betriebssystems, die es verknüpften Prozessen ermöglicht, miteinander zu kommunizieren (Inter-Process-Communication).

Rohre können nur in eine Richtung kommunizieren; Daher können wir sie verwenden, um einen Prozess in die Pipe schreiben zu lassen, während ein anderer aus der Pipe liest. Pipes sind Bereiche des Hauptspeichers, die wie virtuelle Dateien behandelt werden und durch diese Aktion geöffnet werden.

Implementieren Sie mehrere Pipes in C

Betrachten wir ein Beispiel mit 1 Eltern- und 2 Kindprozessen. Wir senden einen Wert, der garantiert vollständig ist, vom übergeordneten Prozess über Pipes an den untergeordneten Prozess, und senden diesen Wert dann mit einem hinzugefügten Inkrement an den übergeordneten Prozess zurück.

Zunächst müssen wir die Bibliotheken importieren, um auf die in unserer Anwendung enthaltenen Klassen und Funktionen zugreifen zu können. Zudem soll eine main()-Funktion entwickelt werden.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char* argv[]) {}

Innerhalb der Funktion main generieren wir Dateideskriptoren für drei Pipes, damit wir in anderen Prozessen lesen und schreiben können. Dann müssen Sie eine Variable vom Typ i als Ganzzahl definieren, um eine for-Schleife einzurichten, die drei Pipes generiert und überprüft, ob der Wert aller Pipes kleiner als Null ist, um an zurückzugeben Fehler.

int fd[3][2];
int i;
for (i = 0; i < 3; i++) {
  if (pipe(fd[i]) < 0) {
    return 1;
  }
}

Nachdem wir die Rohre geöffnet haben, besteht der nächste Schritt darin, mit der Gabelung jedes Prozesses zu beginnen. Als Ergebnis sollten Sie eine Variable namens processIdOne einrichten und dann die Funktion fork() zuweisen.

Führen Sie außerdem eine Überprüfung durch, um sicherzustellen, dass der Wert von processIdOne nicht kleiner als Null ist.

int processIdOne = fork();
if (processIdOne < 0) {
  return 2;
}

Nun prüfen wir, ob der Wert von processIdOne gleich Null ist; Wenn dies der Fall ist, zeigt dies an, dass wir uns im ersten untergeordneten Prozess befinden. Die Funktion close() muss innerhalb des ersten untergeordneten Prozesses verwendet werden, um alle Dateideskriptoren mit Ausnahme des zu diesem Prozess gehörenden zu schließen.

Erstellen Sie eine Integer-Variable mit dem Namen x und geben Sie ihr den Datentyp int. Nun verwenden wir die Methode read(), geben ihr den Dateideskriptor &x und die Grösse zum Lesen des Werts an und prüfen, ob ihr Wert kleiner als Null ist, bevor wir eine Fehlermeldung anzeigen, falls dies der Fall ist Ist.

Als nächstes prüfen wir, ob der Wert fileDescriptor[1][1] kleiner als Null ist und zeigen einen Fehler an, wenn dies der Fall ist. Wir tun dies, indem wir die Methode write() verwenden, ihr ihre Parameter übergeben und mit dem fileDescriptor[1][1] schreiben.

Die Dateideskriptoren für den ersten untergeordneten Prozess sollten im letzten Schritt geschlossen werden.

if (processIdOne == 0) {
  // Child process 1
  close(fileDescriptor[0][1]);
  close(fileDescriptor[1][0]);
  close(fileDescriptor[2][0]);
  close(fileDescriptor[2][1]);
  int x;
  if (read(fileDescriptor[0][0], &x, sizeof(int)) < 0) {
    return 3;
  }
  x += 10;
  if (write(fileDescriptor[1][1], &x, sizeof(int)) < 0) {
    return 4;
  }
  close(fileDescriptor[0][0]);
  close(fileDescriptor[1][1]);
  return 0;
}

Der nächste Schritt besteht darin, eine Variable namens processIdTwo einzurichten und dann die Funktion fork() zuzuweisen. Zusätzlich sollte überprüft werden, ob der Wert von processIdTwo ungleich Null ist.

int processIdTwo = fork();
if (processIdTwo < 0) {
  return 5;
}

Nun prüfen wir, ob die processIdTwo den Wert Null hat; Wenn dies der Fall ist, zeigt dies an, dass wir uns im zweiten untergeordneten Prozess befinden. Innerhalb des zweiten untergeordneten Prozesses müssen wir sicherstellen, dass alle Dateideskriptoren mit der Methode close() geschlossen werden, mit Ausnahme dieses speziellen Prozesses.

Deklarieren Sie eine Variable mit dem Datentyp int namens x.. Jetzt verwenden wir die Methode read(), füttern sie mit dem Dateideskriptor &x und der Größe zum Lesen des Werts und stellen sicher, dass ein Fehler angezeigt wird, wenn der Wert kleiner als Null ist.

Als nächstes wird der Wert von x erhöht, indem der Wert 10 hinzugefügt wird, und wir verwenden die Funktion write() mit ihren Parametern, um mit dem fileDescriptor[2][1] zu schreiben. Wir prüfen dann, ob sein Wert kleiner als Null ist, und wenn ja, zeigen wir eine Fehlermeldung an.

Schließen Sie zuletzt die Dateideskriptoren des zweiten untergeordneten Prozesses.

if (processIdTwo == 0) {
  close(fileDescriptor[0][0]);
  close(fileDescriptor[0][1]);
  close(fileDescriptor[1][1]);
  close(fileDescriptor[2][0]);
  int x;
  if (read(fileDescriptor[1][0], &x, sizeof(int)) < 0) {
    return 6;
  }
  x += 10;
  if (write(fileDescriptor[2][1], &x, sizeof(int)) < 0) {
    return 7;
  }
  close(fileDescriptor[1][0]);
  close(fileDescriptor[2][1]);
  return 0;
}

Wie wir es zuvor getan haben, werden wir jetzt alle anderen Dateideskriptoren im übergeordneten Prozess schließen, mit Ausnahme desjenigen, der diesem Prozess entspricht.

Deklarieren Sie die Variable x als Integer-Typ. Nun verwenden wir die Funktion read(), geben ihr den Dateideskriptor &x und die Grösse zum Lesen des Wertes und prüfen, ob ihr Wert kleiner als Null ist, um festzustellen, ob ein Fehler angezeigt werden soll.

Als nächstes wird der Wert von x erhöht, indem der Wert 10 hinzugefügt wird. Wir verwenden die Funktion write() mit ihren Parametern, um mit dem fileDescriptor[2][0] zu schreiben, und wir prüfen, ob ihr Wert kleiner als Null ist, um zu bestimmen, ob eine Fehlermeldung angezeigt werden soll oder nicht.

Stoppen Sie die offenen Dateideskriptoren, indem Sie sie schließen.

Sowohl für processIdOne als auch für processIdTwo sollte die Methode waitpid() verwendet werden. Diese Funktion wartet, bis alle Prozesse abgeschlossen sind, bevor sie mit der nächsten Operation fortfährt.

close(fileDescriptor[0][0]);
close(fileDescriptor[1][0]);
close(fileDescriptor[1][1]);
close(fileDescriptor[2][1]);
int x = 0;
if (write(fileDescriptor[0][1], &x, sizeof(int)) < 0) {
  return 8;
}
if (read(fileDescriptor[2][0], &x, sizeof(int)) < 0) {
  return 9;
}
printf("The value received from the child processes is: %d\n", x);
close(fileDescriptor[0][1]);
close(fileDescriptor[2][0]);

waitpid(processIdOne, NULL, 0);
waitpid(processIdTwo, NULL, 0);

Vollständiger Quellcode zum Implementieren mehrerer Pipes in C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
  int fileDescriptor[3][2];
  int i;
  for (i = 0; i < 3; i++) {
    if (pipe(fileDescriptor[i]) < 0) {
      return 1;
    }
  }

  int processIdOne = fork();
  if (processIdOne < 0) {
    return 2;
  }

  if (processIdOne == 0) {
    // Child process 1
    close(fileDescriptor[0][1]);
    close(fileDescriptor[1][0]);
    close(fileDescriptor[2][0]);
    close(fileDescriptor[2][1]);
    int x;
    if (read(fileDescriptor[0][0], &x, sizeof(int)) < 0) {
      return 3;
    }
    x += 10;
    if (write(fileDescriptor[1][1], &x, sizeof(int)) < 0) {
      return 4;
    }
    close(fileDescriptor[0][0]);
    close(fileDescriptor[1][1]);
    return 0;
  }

  int processIdTwo = fork();
  if (processIdTwo < 0) {
    return 5;
  }

  if (processIdTwo == 0) {
    // Child process 2
    close(fileDescriptor[0][0]);
    close(fileDescriptor[0][1]);
    close(fileDescriptor[1][1]);
    close(fileDescriptor[2][0]);
    int x;
    if (read(fileDescriptor[1][0], &x, sizeof(int)) < 0) {
      return 6;
    }
    x += 10;
    if (write(fileDescriptor[2][1], &x, sizeof(int)) < 0) {
      return 7;
    }
    close(fileDescriptor[1][0]);
    close(fileDescriptor[2][1]);
    return 0;
  }

  // Parent process
  close(fileDescriptor[0][0]);
  close(fileDescriptor[1][0]);
  close(fileDescriptor[1][1]);
  close(fileDescriptor[2][1]);
  int x = 0;
  if (write(fileDescriptor[0][1], &x, sizeof(int)) < 0) {
    return 8;
  }
  if (read(fileDescriptor[2][0], &x, sizeof(int)) < 0) {
    return 9;
  }
  printf("The value received from the child processes is: %d\n", x);
  close(fileDescriptor[0][1]);
  close(fileDescriptor[2][0]);

  waitpid(processIdOne, NULL, 0);
  waitpid(processIdTwo, NULL, 0);

  return 0;
}

Ausgang:

The value received from the child processes is: 20
Waqar Aslam avatar Waqar Aslam avatar

I am Waqar having 5+ years of software engineering experience. I have been in the industry as a javascript web and mobile developer for 3 years working with multiple frameworks such as nodejs, react js, react native, Ionic, and angular js. After which I Switched to flutter mobile development. I have 2 years of experience building android and ios apps with flutter. For the backend, I have experience with rest APIs, Aws, and firebase. I have also written articles related to problem-solving and best practices in C, C++, Javascript, C#, and power shell.

LinkedIn

Verwandter Artikel - C Pipe