Unix­1 im Wintersemester 1994/95

4.  Übungsblatt

Aufgabe 1  (theoretisch, 1 Punkt)

Wieso existiert kein Programm cd in einem UNIX­System?  Warum ist das Kommando cd als Builtin­Kommando in der Shell implementiert?

Aufgabe 2  (theoretisch, 2 Punkte)

Was genau passiert mit einem Prozeß, der exit() ausführt? Was ist der Unterschied zwischen dieser Funktion und _exit()?

Aufgabe 3  (theoretisch, 2 Punkte)

Was geschieht, wenn ein Prozeß terminiert, dessen Vater schon terminiert ist?  Was würde passieren, wenn einfach niemand darauf reagiert?

Aufgabe 4  (theoretisch, 2 Punkte)

Auf welche der folgenden Datenstrukturen hat ein Prozeß A schreibenden Zugriff, wenn er sich im Zustand user running bzw.  kernel running befindet:

Aufgabe 5 (theoretisch, 3 Punkte)

Der Unix­Kern erreicht die User­Structure des gerade aktiven Prozesses einfach durch Ansprechen der kernglobalen Variablen u (z.B. u.u_error = ENOMEM).  Wie ist es möglich, daß der Kern so immer die richtige User­Structure trifft?  Was passiert in diesem Zusammenhang bei einem Prozeßwechsel?  Wie sieht der Adreßraum eines laufenden Prozesses in etwa aus?  (Zeichnung)

Aufgabe 6 (praktisch, 10 Punkte)

Erweitert Eure Shell msh Die Shell soll nun in der Lage sein, auch andere als Builtin­Kommandos zur Ausführung zu bringen.  Als Erweiterung ist die Quelldatei command.c zu schreiben, in der die Funktion do_command() das ihr übergebene Kommando ausführen soll.  Die Funktion do_command() liefert keinen Rückgabewert.  Zum Auffinden der Kommandos soll die Shell den in der Environment­Variablen PATH gesetzten search path benutzen.  Das Auftreten eines ampersand (&) muß korrekt behandelt werden.

  command.c:
	void do_command (struct kommando *kp)
Beim Start von Prozessen im Hintergrund ist deren Prozeß­ID auszugeben. Die Shell soll die von ihr gestarteten Hintergrundprozesse von Zeit zu Zeit aus dem zombie­state erlösen, damit die Anzahl der für einen Benutzer zulässigen Prozesse im System nicht erreicht wird oder gar die Prozeßtabelle überläuft.  Diese Erlösung soll mit Hilfe des Systemcalls wait3() jeweils vor der Ausgabe des Prompts geschehen.  Schreibt Euch dazu in der Datei command.c die Funktion do_wait(), die auf die Termination eines Kind­Prozesses wartet.

       void do_wait (int pid, int mode)
Die Funktion do_wait() soll auf die Termination des angegebenen Prozesses warten und liefert keinen Rückgabewert.  Bei der Angabe des Argumentes 0 soll auf die Termination des aktuellen Vordergrundprozesses gewartet werden.  Für jeden erlösten Hintergrund­Prozeß soll eine Meldung etwa in der Form

     pid       Done ls -l command.c parser.c
ausgegeben werden.  Dazu muß eine Tabelle angelegt werden, die alle zu einem Hintergrundprozeß notwendigen Informationen aufnehmen kann.  Zu diesen Informationen gehören mindestens die Prozeß­ID, der Name und die Argumente des gestarteten Kommandos.  Der Einfachheit halber ist diese Tabelle statisch anzulegen.  Sie soll aus genau 10 Einträgen bestehen.  Ist die Tabelle voll, so ist die Bearbeitung von Hintergrundprozessen mit einer entsprechenden Fehlermeldung zurückzuweisen.

Fehlermeldungen von Systemcalls wie fork(), execve() usw.  sind mit der Funktion perror() anzuzeigen.  Pipes und Umlenken der Ein­ und Ausgabe müssen erst in der nächsten Aufgabe realisiert werden.