Institut für Informatik
der Ludwig-Maximilians-Universität München

Lehr- und Forschungseinheit für Kommunikationssysteme und Systemprogrammierung

Systempraktikum - Wintersemester 2009/2010

Prof. Dr. Dieter Kranzlmüller
Dr. Thomas Schaaf, Dr. Nils gentschen Felde

Blatt 3- Grundlagen III: Prozesserzeugung, Prozessüberlagerung, Pipes

Abgabedatum theor. Aufgaben Abgabedatum prakt. Aufgaben Deadline Projektaufgaben
12.11. 12.11. -

Hinweise


Theoretische Aufgaben (Blatt 3)


Aufgabe T-3-1

Zeichnen Sie das 7-Zustands-Prozessmodell in Form eines endlichen Automaten (d.h. Zustände als Knoten, Übergänge als gerichtete Kanten/Pfeile). Nummerieren Sie alle Übergänge, und erstellen Sie eine vollständige Tabelle der Form:
Nr. Übergang Beschreibung Beispiel
... ... ... ...
2 Ready ® Running Scheduler wählt Prozess Der Prozess mit der höchsten Priorität
wird zur Ausführung ausgewählt (Priority-Scheduling)
... ... ... ...

Aufgabe T-3-2

Im Zusammenhang mit untereinander verwandten Unix-Prozessen unterscheidet man spezielle Konfigurationen:
  1. Was ist ein Zombie-Prozess, und wie kann er entstehen?
  2. Wann spricht man im Kontext von Prozessen von einem Waisen? Was geschieht mit ihm, wenn er entstanden ist?
  3. Analysieren Sie die folgenden vier Programme. Erklären Sie die Unterschiede, und beschreiben Sie die Folgen daraus. Finden Sie heraus, wo ein Zombie und wo ein Waise deutlich sichtbar wird. Überprüfen Sie Ihre Vermutungen durch Übersetzen und Testen der Programme! Hierbei hilft Ihnen ein mehrfaches ps l | grep < progname > in einem anderen Fenster. Dokumentieren Sie Ihre Ergebnisse ausführlich!
  4. Beantworten Sie die folgenden Fragen zu den einzelnen Programmen:
    1. Zu Programm A: Was geschieht in Zeile 27 ( kill(childPID, SIGINT); ) genau? Gibt es einen Zusammenhang mit der Entstehung oder Vermeidung von Zombies und/oder Waisen?
    2. Zu Programm B: Inwiefern tragen die Zeilen 17 und 25 ( sleep() -Aufrufe) zum Entstehen von Zombies und/oder Waisen bei?
    3. Zu Programm C: Was geschieht in Zeile 23 ( wait(&status); ) genau? Gehen Sie insbesondere auf die Bedeutung der Variablen status ein. Informationen hierzu finden Sie in der Manpage der Funktion wait() .
Programm A:
Listing 1:
        
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
  pid_t childPID;
  int a;

  childPID = fork();
   
  if (childPID == 0 ) {
    for(a=0; a<10; a++) {
      printf("for loop: child PID %d, %i \n", getpid(), a);
      sleep(2);
    }
    printf("child done: child PID %d \n", getpid());
    exit(0);
  }

  for(a=0; a<10; a++) {
    printf( "\t\t\t\t parent: child PID %d, %i\n", childPID, a);
    sleep(1);
  }
  kill(childPID, SIGINT);
  printf("\t\t\t\t parent done: child PID %d, %i\n", childPID, a);
  exit(0);
}

Programm B:
Listing 2:
        
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
  pid_t childPID;
  int a;

  childPID = fork();
   
  if(childPID == 0) {
    for(a=0; a<10; a++) {
      printf("for loop: child PID %d, %i \n", getpid(), a);
      sleep(1);
    }
    printf("child done: child PID %d \n", getpid());
    exit(0);
  }

  for(a=0; a<10; a++) {
    printf("\t\t\t\t parent: child PID %d, %i\n", childPID, a);
    sleep(2);
  }
  printf("\t\t\t\t parent done: child PID %d, %i\n", childPID, a);
  exit(0);
}

Programm C:
Listing 3:
        
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
  pid_t childPID;
  int a, status;

  childPID = fork();
   
  if (childPID == 0 ) {
    for(a=0; a<10; a++) {
      printf("for loop: child PID %d, %i \n", getpid(), a);
      sleep(2);
    }
    printf("child done: child PID %d \n", getpid());
    exit(0);
  }

  wait(&status);
  for(a=0; a<10; a++) {
    printf("\t\t\t\t parent: child PID %d, %i\n", childPID, a);
    sleep(1);
  }
  wait(NULL);
  printf("\t\t\t\t parent done: child PID %d, %i\n", childPID, a);
  exit(0);
}

Programm D:
Listing 4:
        
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
  pid_t childPID;
  int a;

  childPID = fork();
   
  if (childPID == 0 ) {
    for(a=0; a<10; a++) {
      printf("for loop: child PID %d, %i \n", getpid(), a);
      sleep(2);
    }
    printf("child done: child PID %d \n", getpid());
    exit(0);
  }

  for(a=0; a<10; a++) {
    printf("\t\t\t\t parent:   child PID %d, %i\n", childPID, a);
    sleep(1);
  }
  printf("\t\t\t\t parent done: child PID %d, %i\n", childPID, a);
  exit(0);
}


Aufgabe T-3-3

  1. Überlegen Sie sich vier sinnvolle Anforderungen an CPU-Zuteilungsstrategien (Scheduling-Strategien).
  2. Nennen Sie drei preemptive Scheduling-Strategien, und erklären Sie in kurzen Stichworten ihre Arbeitsweise.
  3. Versuchen Sie, die in b) gewählten Strategien hinsichtlich der vier Anforderungen aus a) zu beurteilen. Beurteilen Sie außerdem den Umgang der Strategien mit besonders kurzen sowie mit besonders langen Prozessen.

Praktische Aufgaben (Blatt 3)


Aufgabe P-3-1

  1. Schreiben Sie ein einfaches C-Programm, das in einer Schleife fork() -Aufrufe tätigt. Die Anzahl n der Schleifendurchläufe soll als Aufrufparameter über die Konsole übergeben werden. Jeder erzeugte Kindprozess (sowie der ursprüngliche Vaterprozess) soll genau einmal seine Parent-Prozess-ID (PPID) ausgeben.
  2. Testen Sie Ihr Programm mit verschiedenen Werten für n. Dokumentieren Sie die Ausgabe.
  3. Zeichnen Sie den Prozess-Baum, der bei n : = 4 entsteht. Lesen Sie daraus ab, wie viele unterschiedliche PPIDs jeweils in welcher Häufigkeit in der Ausgabe vorkommen können. Überprüfen Sie anhand der tatsächlichen Ausgabe. Wie viele direkte Kinder, Enkel, Urenkel usw. wurden also jeweils erzeugt?
  4. Geben Sie eine allgemeine Formel für die Zahl der erzeugten Kindprozesse in Abhängigkeit von n an.
  5. Schreiben Sie jetzt ein weiteres C-Programm, das durch einen fork() -Aufruf zunächst einen ersten Kindprozess und anschließend durch einen zweiten fork() -Aufruf einen zweiten Kindprozess erzeugt. Achten Sie aber darauf, dass keiner der beiden Kindprozesse seinerseits Kindprozesse erzeugt! (Es sollen also insgesamt genau zwei direkte Kindprozesse vom ursprünglichen Prozess angelegt werden.) Jeder der beiden Kindprozesse soll nun das Programm aus Teilaufgabe a) einmal ausführen (Aufrufparameter n beliebig). Verwenden Sie einen passenden Aufruf aus der Familie der exec*() -Familie, um die Überlagerung der Kindprozesse zu realisieren.
Bitte denken Sie an ein Makefile!

Aufgabe P-3-2

Schreiben Sie genau ein C-Programm, das eine über die Kommandozeile angegebene Anzahl von Kindprozessen erzeugt, die jeweils mit Hilfe der Funktion getpid() ihre Prozess-ID auf dem Bildschirm ausgeben. Falls kein Argument über die Kommandozeile übergeben wird, sollen vier Kindprozesse erzeugt werden. Lassen Sie jeden Kindprozess dann noch fünf Sekunden pausieren. Geben sie eine Zeichenkette aus, die besagt, welcher Prozess terminiert, bevor dies passiert. Achten Sie darauf, dass die Kindprozesse ihrerseits keine neuen Kindprozesse erzeugen.
Während die Kindprozesse laufen, soll der Elternprozess durch den Systemaufruf execlp() das Unix-Kommando ps lf in einer Schleife zehn mal mit jeweils zwei Sekunden Pause dazwischen ausführen. Benutzen Sie die Funktion sleep() , um die Wartezeiten zu erzeugen. Schreiben Sie getrennte Funktionen für die Aktionen des Elternprozesses und der Kindprozesse.
Bitte denken Sie an ein Makefile!

Aufgabe P-3-3

Schreiben Sie ein C-Programm, das eine uni-direktionale Kommunikation über eine (namenlose) Pipe zwischen einem Vaterprozess und einem Sohnprozess bereitstellt. Hierbei soll der Vaterprozess den Inhalt einer Datei lesen und an den Sohnprozess schicken, der diesen dann am Bildschirm ausgibt. Der Name der zu lesenden Datei soll dabei als Argument übergeben werden.
Bevor der Sohn beginnt, von der Pipe zu lesen, soll er zunächst 5 Sekunden pausieren. Versehen Sie Vater und Sohn mit Ausgabeanweisungen, die anzeigen, welche Aktion sie gerade ausführen (insb. dass sie gestartet bzw. beendet wurden).
Überlegen Sie sich zur Lösung der Aufgabe die folgenden Teilschritte:
Bitte denken Sie an ein Makefile!




File translated from TEX by TTH, version 3.59.
On 30 Oct 2009, 15:59.