Unix-Systemprogrammierung
Literaturhinweise
- Eigentlich sollte mein
Skript reichen.
-
Hier gibt es ein C-Skript bzw. Tutorial,
das auch den gesamten Stoff unserer Unix-Systemprogrammierung abdeckt.
- Bei den Lehrern wird üblicherweise folgendes Buch verwendet:
Helmut Herold: Linux-Unix-Systemprogrammierung
Addison-Wesley 1999, ISBN 3-8273-1512-3
Musterlösungen
Tips & Lieblingsfehler
- Was man aufmacht, macht man zu:
- Zu open und pipe gehört close
(solange der Schreiber eine Pipe nicht zumacht, weiß der Leser nicht,
daß der Schreiber fertig ist!).
- Zu fopen und fdopen (!!!) gehört fclose.
- Zu popen gehört pclose
(ganz wichtig, sonst bleiben Files und Prozesse über!).
- Man macht an jedem Ende einer Pipe entweder ein fdopen
oder ein dup2 (oder gar nichts),
aber niemals beides an einem Ende!
- Merkregel für die Enden einer Pipe:
Die Nummern sind wie bei den Standard-Files stdin und stdout:
0 ist zum Lesen und 1 ist zum Schreiben!
- Variablen für Signal-Handler müssen global deklariert werden,
wenn sie in- und außerhalb des Signal-Handlers verwendet werden sollen,
und lokal static,
wenn sie zwischen Aufrufen des Signal-Handlers erhalten bleiben sollen!
- Wenn man aus einem Signal-Handler normal zurückkehrt (ohne exit),
dann macht das Programm dort weiter,
wo es gerade war (außer, man hat gerade einen Systemaufruf,
der EINTR liefern kann, unterbrochen):
Endlosschleife bleibt Endlosschleife!
- Wir warten niemals mit "Busy Waiting" (d.h. einer Warteschleife),
das ist immer falsch!
- Wenn man fork in einer Schleife macht:
- Der Sohn darf nie zum nächsten Schleifendurchlauf kommen,
sonst werden zu viele Prozesse erzeugt (exit verwenden!)!
- Der Vater-Code darf nicht innerhalb der Schleife stehen,
wenn er nur einmal ausgeführt werden soll, sondern muß nach der Schleife kommen.
- Nach mehreren fork braucht man entsprechend viele wait!
- Man sollte mittels wait immer sicher stellen,
daß der Vaterprozeß erst nach allen Sohnprozessen endet
(außer, man schreibt einen Server, der absichtlich im Hintergrund weiterläuft):
Sonst passiert es, daß die Söhne noch Ausgaben auf das Terminal schreiben
oder Input vom Terminal lesen,
nachdem die Shell schon wieder den Shellprompt angezeigt hat
(die Shell orientiert sich nur am Ende des Vaterprozesses!).
- Bitte immer scharf nachdenken, was beim fork
mit lokalen und globalen Variablen passiert,
und wann man daher ein Array mit einem Element pro Prozeß braucht
(und vor allem: Wann nicht!).
- Variablen (auch globale!) haben eine unabhängige Kopie pro Prozeß:
Wenn man beispielsweise Variablen im Sohnprozeß setzt oder raufzählt
und dann im Vaterprozeß ausgibt, so hat das nicht den gewünschten Effekt!
- Das exec nach einem fork macht man immer im Sohn,
nie im Vater.
Würde man das exec im Vater machen,
kann man kein wait mehr machen,
und damit weder den Exitstatus des gestarteten Programmes ermitteln,
noch ein geordnetes Programmende
(Vaterprozeß nach allen Sohnprozessen) sicherstellen.
- Ein exec kehrt nicht mehr zurück, wenn es funktioniert.
Es ist daher wenig sinnvoll, nach einem exec noch Code zu haben
oder gar innerhalb einer Schleife ein exec
ohne vorheriges fork aufzurufen:
Die Schleife kommt sicher nicht zu ihrem zweiten Durchlauf...
- Was die einzelnen Argumente für ein exec sind,
erkennt man am besten, wenn man sich überlegt,
wo beim Eintippen des Befehls auf der Shell die Zwischenräume wären
(die Shell teilt die Commandline bei den Zwischenräumen in einzelne Argumente).
Auf den Befehl selbst nicht vergessen!
- Bei den Permissions für Shared-Memory-Segmente und Semaphoren
bitte überlegen:
Wer sollte was dürfen? Soll jeder das Shared-Memory-Segment lesen können?
Schreiben können? Soll jeder die Semaphore rauf- und runterzählen können?
- Beim Aufräumen von Shared Memories und Semaphoren sind drei Fälle zu beachten:
- Das Programm endet normal.
- Das Programm endet nach einer Fehlermeldung mit exit.
- Das Programm endet durch ein Signal (kill, Ctrl/C,
abort oder assert, Absturz, ...).
Sinnvolle Vorgehensweise:
- Für die ersten beiden Fälle wird mit atexit ein Exit-Handler
installiert, der das Aufräumen macht.
- Für den letzten Fall wird ein Signal-Handler installiert, der nur ein
simples exit macht. Dadurch wird im Falle eines Signals ebenfalls
der Exit-Handler zum Aufräumen ausgeführt.