ein Kapitel zurück                                           ein Kapitel weiter

In Kapitel zuvor haben wir jetzt viel über ein Programm und seine Arbeitsumgebung gehört (Programm+Arbeitsumgebung=Prozeß). Nun wird es Zeit selbst Prozesse zu erzeugen. Dazu gibt es 2 Möglichkeiten. Zum einen kann ein Prozeß vollkommend durch einen anderen Ausgetauscht werden ohne das die Arbeitsumgebung gewechselt wird. Dazu kommen wir aber später. Die 2. Möglichkeit wäre mit dem Systemaufruf fork......

#include <sys/types>
#include <unistd.h>

pid_t fork(void);  

pid_t ist dabei ein Primitiver Datentyp der für Prozeß-ID oder auch Gruppen-Prozeß-ID steht. Mit einem Aufruf von fork erzeugen wir einen neuen Prozeß (Kindprozeß), der fast identisch ist mit seinem Erzeuger, dem Elternprozeß.

Folgende Merkmale erbt unser Kindprozess von seinen Eltern....

  • Code-,Daten-und Stacksegment des Programms
  • Die Dateitabelle (wer es wieder vergessen hat: In der Dateitabelle befindet sich der Zustand der Filedeskriptor Flags, ob z.B. gelesen oder geschrieben wird. Außerdem befindet sich in der Dateitabelle noch die aktuelle Position des Schreib/Lesezeigers und ein Zeiger auf die v-node-Tabelle. ) insklusive close-on-exec-Flag.
  • Das aktuelle Verzeichnis und das Wurzelverzeichnis (logisch)
  • Reale und effektive Benutzernummer und Gruppennummer
  • Prioritäten des Prozesses (Als root könnten sie da mit nice eingreifen)
  • Kontrollterminal
  • Signalmaske
  • Resourcen-Limits
  • Envronment
  • Shared Memory Segment (mmap)

Folgende Merkmale bekommt das Kind nicht von seinen Eltern......

  • Prozeßindefikation (PID,PPID)
  • Die verbrauchte CPU-Zeit des Kindprozesses wird auf Null gesetzt (logisch)
  • Hängende Signal des Elternprozesses
  • Dateisperren (record locking)

Durch den Aufruf von fork entstehen 2 völlig Identische Prozesse die vom Programm verarbeitet werden. Alle Anweisungen nach fork werden 2 mal ausgeführt. Einmal im Kindprozess und einmal im Elternprozess.

Der Kindprozeß und der Elternprozeß erhalten nach dem Aufruf von fork unterschiedlich Rückgabewerte......

  • Der Elternprozeß bekommt die Prozeßidenfikation (PID) des Kindes als Rückgabewert. Sollte dieser Wert negativ sein liegt ein Fehler durch fork vor.

  • Der Kindprozeß erhält den Wert 0 als Rückgabewert wenn der fork-Aufruf geglückt ist.

Somit können sie das ganze folgendermaßen überprüfen........

switch(ret=fork())
   {
     case -1 :  /*fork() war erfolglos - Fehlermeldung _exit*/
     case 0 :   /*Code für den Kindprozeß*/
     default :  /*Code für den Elternprozeß*/
   }  

Nun wollen wir uns ein Beispiel mit fork ansehen......

/*Download:fork1.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char **argv) { pid_t kind1; while(1) { switch(kind1=fork()) { case -1 : exit(0); break; case 0 : system("konsole -caption 'kind' -vt_sz 20x10"); break; default : printf("Elternprozess: Warte auf des Ende vom Kindprozess!\n"); printf("Um den Prozess %s zu killen gib bitte ein : ",*argv); printf("kill -9 %d (%d=PID)\n",kind1,kind1);exit(1); } } return 0; }

Ich gehe mal davon aus das die meisten Linux unter einer grafischen Oberfläche (KDE, GNOME...) benutzen. Mit....

switch(kind1=fork())  

...führen wir fork aus. Der Rückgabewert steht nun in der Variablen kind1. Nun testen wir ob wir ein Kindprozess starten konnten. Bei -1 brechen wir das Programm gleich wieder ab. Falls wir ein Kindprozess erzeugen konnten öffnen wir in diesem Beispiel mittels....

case 0 : system("konsole -caption 'kind' -vt_sz 20x10");  break;  

...eine neue konsole 'kind' mit der Größe 20x10. Diese Kind besitzt nun sämtlich oben genannten Eigenschaften wie der Elternprozess kann aber nur Code ausführen die für Ihn bestimmt sind (kind1==0). Würden sie jetzt das break entfernen würde unser Kindprozess ein neuen Kindprozess starten und wäre somit der "Vater" von dem neuen Kindprozess und der "Sohn" von dem Elternprozess. Das würde natürlich unendlich viele "Kinder" erzeugen solange bis vor Überbevölkerung des Arbeitspeichers der PC streikt. Also lassen sie break wo es ist! Unsere while(1) läuft erneut durch und nun gibt unser Elternprozess (kind1 > 0) aus wie wir den Kindprozess wieder killen können. Sie können jetzt versuchen das Fenster zu schließen oder die Konsole mit exit schließen oder gar mit....

kill -SIGKILL PID von konsole  

...den Kindprozeß schließen. Geben sie doch in einer der beiden Konsolen mal ps oder ps x ein (oder pstree)
Diese Befehle zeigen Ihnen welchen Prozesse mit welchem Status gerade am laufen sind. Bei ps x sehen z.B.

PID    TTY    STAT    TIME    COMMAND
214    ?         S    0:00      xterm -ls -T Failsave -geometry 80x24-0-0
223    pts/0     SW   0:00      [bash]
..........................................
..........................................
294    pts/1     SW   0:00      [cat]
295    pts/0     S    0:00      knotify
298    pts/0     S    0:00      ksmserver --restore
299    ?         S    0:02      kdeinit: kwin
300    ?         S    0:04      kdeinit: kwrite
678    ?         S    0:01      kdeinit: konsole
679    pts/4     S    0:00      /bin/bash
1019   ?         S    0:07      kdeinit: kwrite
1111   pts/4     S    0:00      fork1
1112   pts/4     S    0:00      konsole -caption kind -vt_sz 20x10
1113   pts/2     S    0:00      /bin/bash
1120   pts/4     R    0:00      ps x  

In diesem Beispiel ist der der Prozess mit PID 1111 und dem Terminal pts/4 Status sleep (S). Das Programm habe gleich fork genannt. fork ist nun eine Prozess in unserem System. Das Fenster das wir mit fork geöffnet haben ist in dem Fall...

1113 pts/2 S 0:00 /bin/bash  






Anhand dieser Zeichnung können sie erkennen, das der Elternprozeß solange wartet bis sich der Kindprozeß beendet. Vorher kann sich der Elternprozeß nicht beenden. Dies läßt sich aber auch vermeiden. Dazu kommen wir noch. Daher blockiert in unserem Programm die Eltern-Konsole bis sich das Kind beendet. Um dies zu vermeiden könnten sie aber auch unser Programm fork1 mit......

./fork1 &  

...im Hintergrund laufen lassen.

Was soll das ganze mit den Prozessen und z.B. fork nun. Das Linux ein Multitasking-System ist das wissen sie ja bereits. Ein Beispiel unter MS-DOS. Sie wollen eine Diskette mit dem Befehl...

format a:\  

...formatieren. Was machen sie nun während die Diskette (was ja einige Zeit dauert) formatiert? Nichts sie müssen warten bis sie fertig sind. Unter Linux können wir ganz einfach das Terminal wechseln oder unter X eine neues Fensterterminal öffnen. Dies Beispiel wollen wir jetzt in unserem Programm einbauen. Unser Kindprozess formatiert eine Diskette mit....

fdformat /dev/fd0 (Low-Level-Formatierung)  

...und unser Vaterprozess öffnet eine neues Fenster mit dem wir weiterarbeiten können...

/*Download:fork2.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char **argv) { pid_t kind1; while(1) { switch(kind1=fork()) { case -1 : exit(0); break; case 0 : system("fdformat /dev/fd0"); exit(1); default : system("konsole -caption 'Neue Konsole'"); exit(2); } } return 0; }

Ähnliches machen sie wenn sie z.B. fdformat folgendermaßen anwenden...

fdformat /dev/fd0 > /dev/null &  

Mit dem Zeichen '&' sorgen sie dafür das der Prozess im Hintergrund ausgeführt wird und mit > /dev/null leiten sie die nervige Ausgabe 'Formatiert n Prozent' ins nichts. Wenn Sie ein Programm mit dem &-Symbol starten, weisen Sie den Befehlsinterpreter an, das Programm im Hintergrund auszuführen.

Dies ermöglicht es dem Befehlsinterpreter, sofort zurückzukehren, so Das Sie weitere Befehle eingeben können. Ohne das &-Symbol wartet der Befehlsinterpreter darauf, Das das Programm beendet wird, bevor er die Eingabe weiterer Befehle erlaubt Manch einer wird jetzt versucht haben in vom Vaterprozess neu geöffneten Fenster mittels ps x zu schauen welche PID unser Prozess 'fdformat ....' hat und dabei wird Ihnen auch aufgefallen sein das der Status von 'fdformat ....' auf S für Sleeping steht. Und das ist auch richtig so, den in dem Moment in dem sie ps x ausführen wird die Formatierung der Diskette für Bruchteile von Sekunden unterbrochen um den Befehl von ps x auszuführen der ja auch ein Prozess darstellt.

Daran können sie auch erkennen das echtes Multitasking nicht möglich ist da ein Prozessor und mag er 10.000 MHZ Taktung haben nicht 2 Sachen auf einmal erledigen kann. Dazu würden wir mehr (mindestens zwei) Prozessoren oder Rechner benötigen. Also denken sie daran wenn sie das Wort Multitasking hören ist dies nur Möglich mit Multiprozessor-Systemen oder mehreren Rechnern (kommt auf das selbe raus). Ändern wir unser Programm nun um damit wir keine Konsole mehr zu öffnen brauchen.....

/*Download:fork3.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char **argv) { pid_t kind1; while(1) { switch(kind1=fork()) { case -1 : exit(0); break; case 0 : system("fdformat /dev/fd0 > /dev/null &"); exit(1); default : printf("Formatieren unterbrechen mit kill -9 %d\n",kind1); exit(2); } } return 0; }

Nun ist die Funktion system aber nicht undingt eine gute Wahl, aber dazu kommen wir noch.

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf