This commit is contained in:
stsh
2022-02-17 11:32:44 +01:00
parent 0765adb9ed
commit 91762d5c9d
192 changed files with 10451 additions and 0 deletions
Binary file not shown.
Binary file not shown.
View File
+5
View File
@@ -0,0 +1,5 @@
[0] Config.pm:311> INFO - This is Biber 2.16
[0] Config.pm:314> INFO - Logfile is 'README.blg'
[131] biber-MSWIN64:340> INFO - ===
[445] Utils.pm:411> ERROR - Cannot find 'README.bcf'!
[445] Biber.pm:132> INFO - ERRORS: 1
View File
+441
View File
@@ -0,0 +1,441 @@
# 07 - Prozesse und Threads
___
![](./ein_mann_orchester.png)
[Quelle: https://www.wikiwand.com/de/Ein-Mann-Orchester](https://www.wikiwand.com/de/Ein-Mann-Orchester)
___
## 1. Übersicht
In diesem Praktikum werden wir uns mit Prozessen, Prozesshierarchien und Threads beschäftigen, um ein gutes Grundverständnis dieser Abstraktionen zu erhalten. Sie werden bestehenden Code analysieren und damit experimentieren. D.h. dies ist nicht ein «Codierungs»-Praktikum, sondern ein «Analyse»- und «Experimentier»-Praktikum.
___
### 1.1 Nachweis
Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.
Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman.
___
## 2. Lernziele
In diesem Praktikum werden Sie sich mit Prozessen, Prozesshierarchien und Threads beschäftigen. Sie erhalten einen vertieften Einblick und Verständnis zur Erzeugung, Steuerung und Terminierung von Prozessen unter Unix/Linux und Sie werden die unterschiedlichen Eigenschaften von Prozessen und Threads kennenlernen.
* Sie können Prozesse erzeugen und die Prozesshierarchie erklären
* Sie wissen was beim Erzeugen eines Prozesses vom Elternprozess vererbt wird
* Sie wissen wie man auf die Terminierung von Kindprozessen wartet
* Sie kennen die Unterschiede zwischen Prozessen und Threads
___
## 3. Aufgaben
Das Betriebssystem bietet Programme um die aktuellen Prozesse und Threads darzustellen.
Die Werkzeuge kommen mit einer Vielzahl von Optionen für die Auswahl und Darstellung der Daten, z.B. ob nur Prozesse oder auch Threads aufgelistet werden sollen, und ob alle Prozesse oder nur die «eigenen» Prozesse ausgewählt werden sollen, etc.
Siehe die entsprechenden `man` Pages für weitere Details.
Eine Auswahl, welche unter Umständen für die folgenden Aufgaben nützlich sind:
| | |
| :-- | :-- |
| `ps` | Auflisten der Prozess Zustände zum gegebenen Zeitpunkt. |
| `pstree` | Darstellung der gesamten Prozesshierarchie. |
| `top` | Wie `ps`, aber die Darstellung wird in Zeitintervallen aufdatiert. |
| `htop` | Wie `top`, aber zusätzlich dazu die Auslastung der CPU in einem System mit mehreren CPUs. |
| `lscpu` | Auflisten der CPUs. |
| `cat`/`proc`/`cpuinfo` | Ähnlich zu `lscpu`, aber mit Zusatzinformationen wie enthaltene CPU Bugs (z.B. `bugs: cpu_meltdown spectre_v1 spect-re_v2 spec_store_bypass l1tf mds swapgs itlb_multihit`) |
___
### 3.1 Aufgabe 1: Prozess mit fork() erzeugen
**Ziele**
* Verstehen, wie mit `fork()` Prozesse erzeugt werden.
* Einfache Prozesshierarchien kennenlernen.
* Verstehen, wie ein Programm, das `fork()` aufruft, durchlaufen wird.
**Aufgaben**
1. Studieren Sie zuerst das Programm `ProcA1.c` und beschrieben Sie was geschieht.
```
```
2. Notieren Sie sich, was ausgegeben wird. Starten Sie das Programm und vergleichen Sie die Ausgabe mit ihren Notizen? Was ist gleich, was anders und wieso?
```
```
___
### 3.2 Aufgabe 2: Prozess mit fork() und exec(): Programm Image ersetzen
**Ziele**
* An einem Beispiel die Funktion `execl()` kennenlernen.
* Verstehen, wie nach `fork()` ein neues Programm gestartet wird.
**Aufgaben**
1. Studieren Sie zuerst die Programme `ProcA2.c` und `ChildProcA2.c`.
2. Starten Sie `ProcA2.e` und vergleichen Sie die Ausgabe mit der Ausgabe unter Aufgabe 1. Diskutieren und erklären Sie was gleich ist und was anders.
```
```
3. Benennen Sie `ChildProcA2.e` auf `ChildProcA2.f` um (Shell Befehl `mv`) und überlegen Sie, was das Programm nun ausgibt. Starten Sie `ProcA2.e` und vergleichen Sie Ihre Überlegungen mit der Programmausgabe.
```
```
4. Nennen Sie das Kindprogramm wieder `ChildProcA2.e` und geben Sie folgenden Befehl ein: `chmod -x ChildProcA2.e`. Starten Sie wiederum `ProcA2.e` und analysieren Sie die Ausgabe von `perror("...")`. Wieso verwenden wir `perror()`?
```
```
___
### 3.3 Aufgabe 3: Prozesshierarchie analysieren
**Ziele**
* Verstehen, was `fork()` wirklich macht.
* Verstehen, was Prozesshierarchien sind.
**Aufgaben**
1. Studieren Sie zuerst Programm `ProcA3.c` und zeichnen Sie die entstehende Prozesshierarchie (Baum) von Hand auf. Starten Sie das Programm und verifizieren Sie ob Ihre Prozesshierarchie stimmt.
2. Mit dem Befehl `ps f` oder `pstree` können Sie die Prozesshierarchie auf dem Bildschirm ausgeben. Damit die Ausgabe von `pstree` übersichtlich ist, müssen Sie in dem Fenster, wo Sie das Programm `ProcA3.e` starten, zuerst die PID der Shell erfragen, z.B. über `echo $$`. Wenn Sie nun den Befehl `pstree -n -p pid-von-oben` eingeben, wird nur die Prozesshierarchie ausgehend von der Bash Shell angezeigt: `-n` sortiert die Prozesse numerisch, `-p` zeigt für jeden Prozess die PID an.
**Hinweis:** alle erzeugten Prozesse müssen arbeiten (d.h. nicht terminiert sein), damit die Darstellung gelingt. Wie wird das im gegebenen Programm erreicht?
___
### 3.4 Aufgabe 4: Zeitlicher Ablauf von Prozessen
**Ziele**
* Verstehen, wie Kind- und Elternprozesse zeitlich ablaufen.
**Aufgaben**
1. Studieren Sie Programm `ProcA4.c.` Starten Sie nun mehrmals hintereinander das Programm `ProcA4.e` und vergleichen Sie die jeweiligen Outputs (leiten Sie dazu auch die Ausgabe auf verschiedene Dateien um). Was schliessen Sie aus dem Resultat?
```
```
**Anmerkung:** Der Funktionsaufruf `selectCPU(0)` erzwingt die Ausführung des Eltern- und Kindprozesses auf CPU 0 (siehe Modul `setCPU.c`). Die Prozedur `justWork(HARD_WORK)` simuliert CPU-Load durch den Prozess (siehe Modul `workerUtils.c`).
___
### 3.5 Aufgabe 5: Waisenkinder (Orphan Processes)
**Ziele**
* Verstehen, was mit verwaisten Kindern geschieht.
**Aufgaben**
1. Analysieren Sie Programm `ProcA5.c`: was läuft ab und welche Ausgabe erwarten Sie?
```
```
2. Starten Sie `ProcA5.e`: der Elternprozess terminiert: was geschieht mit dem Kind?
```
```
3. Was geschieht, wenn der Kindprozess vor dem Elternprozess terminiert? Ändern Sie dazu im `sleep()` Befehl die Zeit von 2 Sekunden auf 12 Sekunden und verfolgen Sie mit top das Verhalten der beiden Prozesse, speziell auch die Spalte S.
```
```
___
### 3.6 Aufgabe 6: Terminierte, halbtote Prozesse (Zombies)
**Ziele**
* Verstehen, was ein Zombie ist.
* Eine Möglichkeit kennenlernen, um Zombies zu verhindern.
**Aufgaben**
1. Analysieren Sie das Programm `ProcA6.c`.
2. Starten Sie das Script `mtop` bzw. `mtop aaaa.e`. Es stellt das Verhalten der Prozesse dynamisch dar.
**Hinweis:** `<defunct>` = Zombie.
3. Starten Sie `aaaa.e` und verfolgen Sie im `mtop`-Fenster was geschieht. Was beachten Sie?
```
```
4. In gewissen Fällen will man nicht auf die Terminierung eines Kindes mit `wait()`, bzw. `waitpid()` warten. Überlegen Sie sich, wie Sie in diesem Fall verhindern können, dass ein Kind zum Zombie wird.
```
```
___
### 3.7 Aufgabe 7: Auf Terminieren von Kindprozessen warten
**Vorbemerkung:** Diese Aufgabe verwendet Funktionen welche erst in der Vorlesung über *Inter-Process-Communication (IPC)* im Detail behandelt werden.
Sie können diese Aufgabe bis dann aufsparen oder die verwendeten Funktionen selber via `man` Pages im benötigten Umfang kennenlernen: `man 2 kill` und `man 7 signal`.
**Ziele**
* Verstehen, wie Informationen zu Kindprozessen abgefragt werden können.
* Die Befehle `wait()` und `waitpid()` verwenden können.
**Aufgaben**
1. Starten Sie das Programm `ProcA7.e` und analysieren Sie wie die Ausgabe im Hauptprogramm zustande kommt und was im Kindprozess `ChildProcA7.c` abläuft.
```
```
2. Starten Sie `ProcA7.e` und danach nochmals mit `1` als erstem Argument. Dieser Argument Wert bewirkt, dass im Kindprozess ein ”Segmentation Error” erzeugt wird, also eine Speicherzugriffsverletzung. Welches Signal wird durch die Zugriffsverletzung an das Kind geschickt? Diese Information finden Sie im Manual mit `man 7 signal`. Schalten Sie nun core dump ein (siehe README) und starten Sie `ProcA7.e 1` erneut und analysieren Sie die Ausgabe.
```
```
**Hinweis:** ein core Dump ist ein Abbild des Speichers z.B. zum Zeitpunkt, wenn das Programm abstürzt (wie oben mit der Speicher Zugriff Verletzung). Der Dump wird im File **core** abgelegt und kann mit dem **gdb** (GNU-Debugger) gelesen werden (siehe `README`). Tippen Sie nach dem Starten des Command Line UI des `gdb where` gefolgt von list ein, damit sie den Ort des Absturzes sehen. Mit `quit` verlassen Sie **gdb** wieder.
3. Wenn Sie `ProcA7.e 2` starten, sendet das Kind das Signal 30 an sich selbst. Was geschieht?
```
```
4. Wenn Sie `ProcA7.e 3` starten, sendet ProcA7.e das Signal SIGABRT (abort) an das Kind: was geschieht in diesem Fall?
```
```
5. Mit `ProcA7.e 4` wird das Kind gestartet und terminiert nach 5 Sekunden. Analysieren Sie wie in ProcA7.e der Lauf- bzw. Exit-Zustand des Kindes abgefragt wird (siehe dazu auch `man 3 exit`).
```
```
___
### 3.8 Aufgabe 8: Kindprozess als Kopie des Elternprozesses
**Ziele**
* Verstehen, wie Prozessräume vererbt werden.
* Unterschiede zwischen dem Prozessraum von Eltern und Kindern erfahren.
**Aufgaben**
1. Analysieren Sie Programm `ProcA8_1.c`: was gibt das Programm aus?
* Starten Sie `ProcA8_1.e `und überprüfen Sie Ihre Überlegungen.
* Waren Ihre Überlegungen richtig? Falls nicht, was könnten Sie falsch überlegt haben?
```
```
2. Analysieren Sie Programm `ProcA8_2.c`: was gibt das Programm aus?
* Starten Sie `ProcA8_2.e` und überprüfen Sie Ihre Überlegungen.
* Waren Ihre Überlegungen richtig? Falls nicht, was könnten Sie falsch gemacht haben?
* Kind und Eltern werden in verschiedener Reihenfolge ausgeführt: ist ein Unterschied ausser der Reihenfolge festzustellen?
```
```
3. Analysieren Sie Programm `ProcA8_3.c` und Überlegen Sie, was in die Datei `AnyOutPut.txt` geschrieben wird, wer schreibt alles in diese Datei (sie wird ja vor `fork()` geöffnet) und wieso ist das so?
* Starten Sie `ProcA8_3.e` und überprüfen Sie Ihre Überlegungen.
* Waren Ihre Überlegungen richtig? Falls nicht, wieso nicht?
```
```
___
### 3.9 Aufgabe 9: Unterschied von Threads gegenüber Prozessen
**Ziele**
* Den Unterschied zwischen Thread und Prozess kennenlernen.
* Problemstellungen um Threads kennenlernen.
* Die `pthread`-Implementation kennen lernen.
**Aufgaben**
1. Studieren Sie Programm `ProcA9.c` und überlegen Sie, wie die Programmausgabe aussieht. Vergleichen Sie Ihre Überlegungen mit denjenigen aus Aufgabe 8.2 b) (`Pro-cA8_2.e`).
* Starten Sie `ProcA9.e` und vergleichen das Resultat mit Ihren Überlegungen.
* Was ist anders als bei `ProcA8_2.e`?
```
```
2. Setzen Sie in der Thread-Routine vor dem Befehl `pthread_exit()` eine unendliche Schleife ein, z.B. `while(1) { }`; .
* Starten Sie das Programm und beobachten Sie das Verhalten mit `top`. Was beobachten Sie und was schliessen Sie daraus?
**Hinweis:** wenn Sie in `top` den Buchstaben H eingeben, werden die Threads einzeln dargestellt.
* Kommentieren Sie im Hauptprogram die beiden `pthread_join()` Aufrufe aus und starten Sie das Programm. Was geschieht? Erklären Sie das Verhalten.
```
```
___
### 3.10 Aufgabe 10 (optional):
#### 3.10.1 Übersicht
Dieser Teil des Praktikums behandelt spezielle Prozesse: die Dämon Prozesse («daemon pro-cesses»). Es ist gedacht als Zusatz zum Basis Praktikum über Prozesse und Threads.
Auch dieser Teil ist ein «Analyse»- und «Experimentier»-Praktikum.
___
##### 3.10.1.1 Nachweis
Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.
Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman.
___
#### 3.10.2 Lernziele
In diesem Praktikum werden Sie sich mit Dämon Prozessen beschäftigen.
* Sie können die Problemstellung der Dämon Prozesse erklären
* Sie können einen Dämon Prozess kreieren
* Sie können aus dem Dämon Prozess mit der Umgebung kommunizieren
*
___
#### 3.10.3 Aufgabe: Dämon Prozesse
**Ziele**
* Problemstellungen um Daemons kennenlernen:
* wie wird ein Prozess zum Daemon?
* wie erreicht man, dass nur ein Daemon vom gleichen Typ aktiv ist?
* wie teilt sich ein Daemon seiner Umwelt mit?
* wo "lebt" ein Daemon?
**Einleitung**
Für diese Aufgabe haben wir einen Daemon implementiert: **MrTimeDaemon** gibt auf Anfrage die Systemzeit Ihres Rechners bekannt. Abfragen können Sie diese Zeit mit dem Programm `WhatsTheTimeMr localhost`. Die Kommunikation zwischen den beiden Prozessen haben wir mit TCP/IP Sockets implementiert. Weitere Infos zum Daemon finden Sie nach den Aufgaben.
Im Abschnitt 4 finden Sie Zusatzinformationen über diese Implementation eines Dämon Prozesses plus weiterführende Informationen.
**Aufgaben**
1. Für die folgende Aufgabe benötigen Sie mindestens zwei Fenster (Kommandozeilen-Konsolen). Übersetzen Sie die Programme mit `make` und starten Sie das Programm **PlapperMaul** in einem der Fenster. Das Programm schreibt (ca.) alle 0.5 Sekunden *Hallo, ich bins.... Pidi* plus seine Prozess-ID auf den Bildschirm. Mit dem Shell Befehl `ps` können Sie Ihre aktiven Prozesse auflisten, auch **PlapperMaul**. Überlegen Sie sich zuerst, was mit **PlapperMaul** geschieht, wenn Sie das Fenster schliessen: läuft **PlapperMaul** weiter? Was geschieht mit **PlapperMaul** wenn Sie sich ausloggen und wieder einloggen? Testen Sie Ihre Überlegungen, in dem Sie die entsprechenden Aktionen durchführen. Stimmen Ihre Überlegungen?
```
```
2. Starten Sie nun das Programm bzw. den Daemon **MrTimeDaemon**. Stellen Sie die gleichen Überlegungen an wie mit **PlapperMaul** und testen Sie wiederum, ob Ihre Überlegungen stimmen. Ob **MrTimeDaemon** noch läuft können Sie feststellen, indem Sie die Zeit abfragen oder den Befehl `ps ajx | grep MrTimeDaemon` eingeben: was fällt Ihnen am Output auf? Was schliessen Sie aus Ihren Beobachtungen?
```
```
3. Starten Sie **MrTimeDaemon** erneut, was geschieht?
```
```
4. Stoppen Sie nun **MrTimeDaemon** mit `killall MrTimeDaemon`.
5. Starten Sie **MrTimeDaemon** und fragen Sie mit `WhatsTheTimeMr localhost` oder mit `WhatsTheTimeMr 127.0.0.1` die aktuelle Zeit auf Ihrem Rechner ab.
**Optional:**
Fragen Sie die Zeit bei einem Ihrer Kollegen ab. Dazu muss beim Server (dort wo **MrTimeDaemon** läuft) ev. die Firewall angepasst werden. Folgende Befehle müssen dazu mit **root-Privilegien** ausgeführt werden:
```bash
iptables-save > myTables.txt # sichert die aktuelle Firewall
iptables -I INPUT 1 -p tcp --dport 65534 -j ACCEPT
iptables -I OUTPUT 2 -p tcp --sport 65534 -j ACCEPT
```
Nun sollten Sie über die IP-Nummer oder über den Rechner-Namen auf den **TimeServer** mit `WhatsTheTimeMr` zugreifen können.
Die Firewall können Sie mit folgendem Befehl wiederherstellen:
```bash
iptables-restore myTables.txt
```
6. Studieren Sie `MrTimeDaemon.c`, `Daemonizer.c` und `TimeDaemon.c` und analysieren Sie, wie die Daemonisierung abläuft. Entfernen Sie die Kommentare im Macro `Out-PutPIDs` am Anfang des Moduls `Daemonizer.c`. Übersetzen Sie die Programme mit make und starten Sie `MrTimeDaemon` erneut. Analysieren Sie die Ausgabe, was fällt Ihnen auf? Notieren Sie alle für die vollständige Daemonisierung notwendigen Schritte.
```
```
7. Setzen Sie beim Aufruf von `Daemonizer()` in `MrTimeDaemon.c` anstelle von `lock-FilePath` den Null-Zeiger `NULL` ein. Damit wird keine lock-Datei erzeugt. Übersetzen Sie die Programme und starten Sie erneut `MrTimedaemon`. Was geschieht bzw. wie können Sie feststellen, was geschehen ist?
**Hinweis:** lesen Sie das log-File: `/tmp/timeDaemon.log.`
```
```
Wenn Sie noch Zeit und Lust haben: messen Sie die Zeit, zwischen Start der Zeitanfrage und Eintreffen der Antwort. Dazu müssen Sie die Datei `WhatsTheTimeMr.c` entsprechend anpassen.
___
#### 3.10.4 Zusatzinformationen
___
##### 3.10.4.1 Diese Implementation
Dieser Daemon besteht aus den 3 Komponenten.
**Hauptprogramm: MrTimeDaemon.c**
Hier werden die Pfade für die lock-Datei, die log-Datei und der ”Aufenthaltsort” des Daemons gesetzt. Die lock-Datei wird benötigt um sicherzustellen, dass der Daemon nur einmal gestartet werden kann. In die lock-Datei schreibt der Daemon z.B. seine PID und sperrt sie dann für Schreiben. Wird der Daemon ein zweites Mal gestartet und will seine PID in diese Datei schreiben, erhält er eine Fehlermeldung und terminiert (es soll ja nur ein Daemon arbeiten). Terminiert der Daemon, wird die Datei automatisch freigegeben. Weil Daemonen sämtliche Kontakte mit ihrer Umwelt im Normalfall abbrechen und auch kein Kontrollterminal besitzen, ist es sinnvoll, zumindest die Ausgabe des Daemons in eine log-Datei umzuleiten. Dazu stehen einige Systemfunktionen für Logging zur Verfügung. Der Einfachheit halber haben wir hier eine normale Datei im Verzeichnis `/tmp` gewählt.
> **Anmerkung:** die Wahl des Verzeichnisses `/tmp` für die lock- und log-Datei ist für den normalen Betrieb problematisch, weil der Inhalt dieses Verzeichnisses jederzeit gelöscht werden kann, bzw. darf. Wir haben dieses Verzeichnis gewählt, weil wir die beiden Dateien nur für die kurze Zeit des Praktikums benötigen.
Der Daemon erbt sein Arbeitsverzeichnis vom Elternprozesse, er sollte deshalb in ein festes Verzeichnis des Systems wechseln, um zu verhindern, dass er sich in einem montierten (gemounteten) Verzeichnis aufhält, das dann beim Herunterfahren nicht demontiert werden könnte (wir haben hier wiederum `/tmp` gewählt).
**Daemonizer: Daemonizer.c**
Der Daemonizer macht aus dem aktuellen Prozess einen Daemon. Z.B. sollte er Signale (eine Art Softwareinterrupts) ignorieren: wenn Sie die CTRL-C Taste während dem Ausführen eines Vordergrundprozess drücken, erhält dieser vom Betriebssystem das Signal SIGINT und bricht seine Ausführung ab. Weiter sollte er die Dateierzeugungsmaske auf 0 setzen (Dateizugriffsrechte), damit kann er beim Öffnen von Dateien beliebige Zugriffsrechte verlangen (die Dateierzeugungsmaske erbt er vom Elternprozess). Am Schluss startet der Daemonizer das eigentliche Daemonprogramm: TimeDaemon.e.
**Daemonprogramm: TimeDaemon.c**
Das Daemonprogramm wartet in einer unendlichen Schleife auf Anfragen zur Zeit und schickt die Antwort an den Absender zurück. Die Datenkommunikation ist, wie schon erwähnt, mit Sockets implementiert, auf die wir aber im Rahmen dieses Praktikums nicht weiter eingehen wollen (wir stellen lediglich Hilfsfunktionen zur Verfügung).
___
##### 3.10.4.2 Zusatzinformation zu Dämon Prozessen
Dämonen oder englisch Daemons sind eine spezielle Art von Prozessen, die vollständig unabhängig arbeiten, d.h. ohne direkte Interaktion mit dem Anwender. Dämonen sind Hintergrundprozesse und terminieren i.A. nur, wenn das System heruntergefahren wird oder abstürzt. Dämonen erledigen meist Aufgaben, die periodisch ausgeführt werden müssen, z.B. Überwachung von Systemkomponenten, abfragen, ob neue Mails angekommen sind, etc.
Ein typisches Beispiel unter Unix ist der Printer Daemon `lpd`, der periodisch nachschaut, ob ein Anwender eine Datei zum Ausdrucken hinterlegt hat. Wenn ja, schickt er die Datei auf den Drucker.
Hier wird eine weitere Eigenschaft von Daemons ersichtlich: meist kann nur ein Dämon pro Aufgabe aktiv sein: stellen Sie sich vor, was passiert, wenn zwei Druckerdämonen gleichzeitig arbeiten. Andererseits muss aber auch dafür gesorgt werden, dass ein Dämon wieder gestartet wird, falls er stirbt.
___
___
## 4. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
| Aufgabe | Kriterium | Punkte |
| :-- | :-- | :-- |
| | Sie können die gestellten Fragen erklären. | |
| 1 | Prozess mit `fork()` erzeugen | 0.5 |
| 2 | Prozess mit `fork()` und `exec()`: Programm Image ersetzen | 0.5 |
| 3 | Prozesshierarchie analysieren | 0.5 |
| 4 | Zeitlicher Ablauf von Prozessen | 0.5 |
| 5 | Waisenkinder (Orphan Processes) | 0.5 |
| 6 | Terminierte, halbtote Prozesse (Zombies) | 0.5 |
| 7 | Auf Terminieren von Kindprozessen warten | 0.5 |
| 8 | Kindprozess als Kopie des Elternprozesses | 0.5 |
| 9 | Unterschied von Threads gegenüber Prozessen | 0.5 |
| 10 | Dämon Prozesse | (4) |
___
Version: 11.01.2022
+149
View File
@@ -0,0 +1,149 @@
# 09/02 - Dämon Prozesse
___
![](./daemon.png)
___
## 1. Übersicht
Dieser Teil des Praktikums behandelt spezielle Prozesse: die Dämon Prozesse («daemon pro-cesses»). Es ist gedacht als Zusatz zum Basis Praktikum über Prozesse und Threads.
Auch dieser Teil ist ein «Analyse»- und «Experimentier»-Praktikum.
___
### 1.1 Nachweis
Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.
Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman.
___
## 2. Lernziele
In diesem Praktikum werden Sie sich mit Dämon Prozessen beschäftigen.
* Sie können die Problemstellung der Dämon Prozesse erklären
* Sie können einen Dämon Prozess kreieren
* Sie können aus dem Dämon Prozess mit der Umgebung kommunizieren
*
___
## 3. Aufgabe: Dämon Prozesse
**Ziele**
* Problemstellungen um Daemons kennenlernen:
* wie wird ein Prozess zum Daemon?
* wie erreicht man, dass nur ein Daemon vom gleichen Typ aktiv ist?
* wie teilt sich ein Daemon seiner Umwelt mit?
* wo "lebt" ein Daemon?
**Einleitung**
Für diese Aufgabe haben wir einen Daemon implementiert: **MrTimeDaemon** gibt auf Anfrage die Systemzeit Ihres Rechners bekannt. Abfragen können Sie diese Zeit mit dem Programm `WhatsTheTimeMr localhost`. Die Kommunikation zwischen den beiden Prozessen haben wir mit TCP/IP Sockets implementiert. Weitere Infos zum Daemon finden Sie nach den Aufgaben.
Im Abschnitt 4 finden Sie Zusatzinformationen über diese Implementation eines Dämon Prozesses plus weiterführende Informationen.
**Aufgaben**
1. Für die folgende Aufgabe benötigen Sie mindestens zwei Fenster (Kommandozeilen-Konsolen). Übersetzen Sie die Programme mit `make` und starten Sie das Programm **PlapperMaul** in einem der Fenster. Das Programm schreibt (ca.) alle 0.5 Sekunden *Hallo, ich bins.... Pidi* plus seine Prozess-ID auf den Bildschirm. Mit dem Shell Befehl `ps` können Sie Ihre aktiven Prozesse auflisten, auch **PlapperMaul**. Überlegen Sie sich zuerst, was mit **PlapperMaul** geschieht, wenn Sie das Fenster schliessen: läuft **PlapperMaul** weiter? Was geschieht mit **PlapperMaul** wenn Sie sich ausloggen und wieder einloggen? Testen Sie Ihre Überlegungen, in dem Sie die entsprechenden Aktionen durchführen. Stimmen Ihre Überlegungen?
```
```
2. Starten Sie nun das Programm bzw. den Daemon **MrTimeDaemon**. Stellen Sie die gleichen Überlegungen an wie mit **PlapperMaul** und testen Sie wiederum, ob Ihre Überlegungen stimmen. Ob **MrTimeDaemon** noch läuft können Sie feststellen, indem Sie die Zeit abfragen oder den Befehl `ps ajx | grep MrTimeDaemon` eingeben: was fällt Ihnen am Output auf? Was schliessen Sie aus Ihren Beobachtungen?
```
```
3. Starten Sie **MrTimeDaemon** erneut, was geschieht?
```
```
4. Stoppen Sie nun **MrTimeDaemon** mit `killall MrTimeDaemon`.
5. Starten Sie **MrTimeDaemon** und fragen Sie mit `WhatsTheTimeMr localhost` oder mit `WhatsTheTimeMr 127.0.0.1` die aktuelle Zeit auf Ihrem Rechner ab.
**Optional:**
Fragen Sie die Zeit bei einem Ihrer Kollegen ab. Dazu muss beim Server (dort wo **MrTimeDaemon** läuft) ev. die Firewall angepasst werden. Folgende Befehle müssen dazu mit **root-Privilegien** ausgeführt werden:
```bash
iptables-save > myTables.txt # sichert die aktuelle Firewall
iptables -I INPUT 1 -p tcp --dport 65534 -j ACCEPT
iptables -I OUTPUT 2 -p tcp --sport 65534 -j ACCEPT
```
Nun sollten Sie über die IP-Nummer oder über den Rechner-Namen auf den **TimeServer** mit `WhatsTheTimeMr` zugreifen können.
Die Firewall können Sie mit folgendem Befehl wiederherstellen:
```bash
iptables-restore myTables.txt
```
6. Studieren Sie `MrTimeDaemon.c`, `Daemonizer.c` und `TimeDaemon.c` und analysieren Sie, wie die Daemonisierung abläuft. Entfernen Sie die Kommentare im Macro `Out-PutPIDs` am Anfang des Moduls `Daemonizer.c`. Übersetzen Sie die Programme mit make und starten Sie `MrTimeDaemon` erneut. Analysieren Sie die Ausgabe, was fällt Ihnen auf? Notieren Sie alle für die vollständige Daemonisierung notwendigen Schritte.
```
```
7. Setzen Sie beim Aufruf von `Daemonizer()` in `MrTimeDaemon.c` anstelle von `lock-FilePath` den Null-Zeiger `NULL` ein. Damit wird keine lock-Datei erzeugt. Übersetzen Sie die Programme und starten Sie erneut `MrTimedaemon`. Was geschieht bzw. wie können Sie feststellen, was geschehen ist?
**Hinweis:** lesen Sie das log-File: `/tmp/timeDaemon.log.`
```
```
Wenn Sie noch Zeit und Lust haben: messen Sie die Zeit, zwischen Start der Zeitanfrage und Eintreffen der Antwort. Dazu müssen Sie die Datei `WhatsTheTimeMr.c` entsprechend anpassen.
___
## 4. Zusatzinformationen
___
### 4.1 Diese Implementation
Dieser Daemon besteht aus den 3 Komponenten.
**Hauptprogramm: MrTimeDaemon.c**
Hier werden die Pfade für die lock-Datei, die log-Datei und der ”Aufenthaltsort” des Daemons gesetzt. Die lock-Datei wird benötigt um sicherzustellen, dass der Daemon nur einmal gestartet werden kann. In die lock-Datei schreibt der Daemon z.B. seine PID und sperrt sie dann für Schreiben. Wird der Daemon ein zweites Mal gestartet und will seine PID in diese Datei schreiben, erhält er eine Fehlermeldung und terminiert (es soll ja nur ein Daemon arbeiten). Terminiert der Daemon, wird die Datei automatisch freigegeben. Weil Daemonen sämtliche Kontakte mit ihrer Umwelt im Normalfall abbrechen und auch kein Kontrollterminal besitzen, ist es sinnvoll, zumindest die Ausgabe des Daemons in eine log-Datei umzuleiten. Dazu stehen einige Systemfunktionen für Logging zur Verfügung. Der Einfachheit halber haben wir hier eine normale Datei im Verzeichnis `/tmp` gewählt.
> **Anmerkung:** die Wahl des Verzeichnisses `/tmp` für die lock- und log-Datei ist für den normalen Betrieb problematisch, weil der Inhalt dieses Verzeichnisses jederzeit gelöscht werden kann, bzw. darf. Wir haben dieses Verzeichnis gewählt, weil wir die beiden Dateien nur für die kurze Zeit des Praktikums benötigen.
Der Daemon erbt sein Arbeitsverzeichnis vom Elternprozesse, er sollte deshalb in ein festes Verzeichnis des Systems wechseln, um zu verhindern, dass er sich in einem montierten (gemounteten) Verzeichnis aufhält, das dann beim Herunterfahren nicht demontiert werden könnte (wir haben hier wiederum `/tmp` gewählt).
**Daemonizer: Daemonizer.c**
Der Daemonizer macht aus dem aktuellen Prozess einen Daemon. Z.B. sollte er Signale (eine Art Softwareinterrupts) ignorieren: wenn Sie die CTRL-C Taste während dem Ausführen eines Vordergrundprozess drücken, erhält dieser vom Betriebssystem das Signal SIGINT und bricht seine Ausführung ab. Weiter sollte er die Dateierzeugungsmaske auf 0 setzen (Dateizugriffsrechte), damit kann er beim Öffnen von Dateien beliebige Zugriffsrechte verlangen (die Dateierzeugungsmaske erbt er vom Elternprozess). Am Schluss startet der Daemonizer das eigentliche Daemonprogramm: TimeDaemon.e.
**Daemonprogramm: TimeDaemon.c**
Das Daemonprogramm wartet in einer unendlichen Schleife auf Anfragen zur Zeit und schickt die Antwort an den Absender zurück. Die Datenkommunikation ist, wie schon erwähnt, mit Sockets implementiert, auf die wir aber im Rahmen dieses Praktikums nicht weiter eingehen wollen (wir stellen lediglich Hilfsfunktionen zur Verfügung).
___
### 4.2 Zusatzinformation zu Dämon Prozessen
Dämonen oder englisch Daemons sind eine spezielle Art von Prozessen, die vollständig unabhängig arbeiten, d.h. ohne direkte Interaktion mit dem Anwender. Dämonen sind Hintergrundprozesse und terminieren i.A. nur, wenn das System heruntergefahren wird oder abstürzt. Dämonen erledigen meist Aufgaben, die periodisch ausgeführt werden müssen, z.B. Überwachung von Systemkomponenten, abfragen, ob neue Mails angekommen sind, etc.
Ein typisches Beispiel unter Unix ist der Printer Daemon `lpd`, der periodisch nachschaut, ob ein Anwender eine Datei zum Ausdrucken hinterlegt hat. Wenn ja, schickt er die Datei auf den Drucker.
Hier wird eine weitere Eigenschaft von Daemons ersichtlich: meist kann nur ein Dämon pro Aufgabe aktiv sein: stellen Sie sich vor, was passiert, wenn zwei Druckerdämonen gleichzeitig arbeiten. Andererseits muss aber auch dafür gesorgt werden, dass ein Dämon wieder gestartet wird, falls er stirbt.
___
## 5. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
| Aufgabe | Kriterium | Punkte |
| :-- | :-- | :-- |
| | Sie können die gestellten Fragen erklären. | |
| 1 | Dämon Prozesse | 4 |
___
Version: 11.01.2022
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 140 KiB

@@ -0,0 +1,178 @@
/******************************************************************************
* File: Daemonizer.c
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: Einen Daemon-Prozess erzeugen
******************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
//*****************************************************************************
// Macro to write out all the PIDs...
#define OutPutPIDs() /*printf("\nPID %d, PPID %d, GRP-ID %d\n", \
getpid(), getppid(), getpgrp())*/
//*****************************************************************************
// Function: Locks file with file descriptor fd
// Returns: 0 on success, -1 of file is already locked
// Exits: on fatal errors
//*****************************************************************************
int lock(int fd) {
int retval, len;
struct flock lock; // data structure for file lock
char buffer[16];
// prepare lockfile
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
retval = fcntl(fd, F_SETLK, &lock); // set file lock
if (retval < 0) {
if ((errno == EACCES) || (errno == EAGAIN)) {
return(-1); // Daemon already runs
}
else {
perror("fatal error when locking file");
exit(-1);
}
}
// empty the lockfile
retval = ftruncate(fd, 0);
if (retval < 0) {
perror("fatal error when emptying lockfile");
exit(-1);
}
// write process ID to lockfile
sprintf(buffer, "%d\n", getpid());
len = strlen(buffer);
retval = write(fd, buffer, len) < len;
if (retval < 0) {
perror("fatal error when writing pid to lockfile");
exit(-1);
}
// set lockfile to close on exec
retval = fcntl(fd, F_GETFD, 0);
if (retval < 0) {
perror("fatal error when reading lockfile flags");
exit(-1);
}
retval = retval | FD_CLOEXEC;
retval = fcntl(fd, F_SETFD, retval);
if (retval < 0) {
perror("fatal error when setting lockfile flags");
exit(-1);
}
return(0);
}
//*****************************************************************************
// Function: Makes a deamon process and runs a daemon function
// Parameter: Daemon function
// data pointer to data to be passed to Daemonfunction
// LogFile, path of logfile, if NULL, no logfile is created
// LivDir, path, where daemon will live
// Returns: should not return
// Exits: if daemon is already runnung or on fatal errors
//*****************************************************************************
int Daemonizer(void Daemon(void *), void *data,
const char *LockFile, const char *LogFile, const char *LivDir) {
pid_t PID;
int fd, dummyfd, retval;
// create a prozess and terminate parents -> parent is init
OutPutPIDs();
PID = fork();
if (PID < 0) {
perror("could not fork()");
exit(-1);
}
else if (PID > 0) {
exit(0); // I have done my work an can exit
}
// ----------------------------------------------------------------------
// now I am a process detached from the parent
// an make the process -> a daemon process
signal(SIGINT, SIG_IGN); // ignore CTRL-C
signal(SIGQUIT, SIG_IGN); // ignore quit
signal(SIGHUP, SIG_IGN); // ignore hangup of terminal
OutPutPIDs();
setsid(); // make process session leader
// and processgroup leader
// no control terminal with pocess
OutPutPIDs();
chdir(LivDir); // change to secure directory
umask(0); // allow all access rights for files
// set up lockfile, if required
if (LockFile != NULL) {
fd = open(LockFile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
S_IRGRP | S_IROTH);
if (fd < 0) {
perror("fatal error when opening lockfile");
exit(-1);
}
retval = lock(fd);
if (retval < 0) {
printf("\n*** daemon is already running ***\n");
exit(0);
}
}
// last message from daemon
printf("\n*** daemon starts with process id: %d ***\n",getpid());
// close "communication" to outer world and set up logging, if required
close(1); // close stdout
close(2); // close stderr
if (LogFile != NULL) { // open log file on stdout
enum {UserWrite=0644};
dummyfd = open(LogFile, O_CREAT | O_APPEND | O_WRONLY,UserWrite);
if (dummyfd < 0) {
perror("could not open log file");
exit(-1);
}
dup(1); // connect stderr to logfile
}
close(0); // now close stdin
// now start the daemon funktion
Daemon(data);
// should not come here
return(0);
}
//*****************************************************************************
@@ -0,0 +1,15 @@
/*********************************************************************
* File: Daemonizer.h
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: einen Daemon-Prozess erzeugen
*********************************************************************/
#ifndef DAEMONIZER_H
#define DAEMONIZER_H
int Daemonizer(void Daemon(void *), void *data,
const char *LockFile, const char *LogFile, const char *LivDir);
#endif
//------------------------------------------------------------------------
@@ -0,0 +1,188 @@
//*****************************************************************************
// ipCom.c IP Socket Functions
// Original Author: M. Thaler, M. Pellaton (Modul BSY)
//*****************************************************************************
//*****************************************************************************
// system includes
//*****************************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
//*****************************************************************************
// local definitions
//*****************************************************************************
#include "IPsockCom.h"
//*****************************************************************************
// Function: send data buffer to host "host" with port "port"
// Parameter: hostname or IP address in dot format
// port number
// buffer
// size of buffer
// Returns: number of characters read on success, -1 if connection failed
// buffer: time data
//
//*****************************************************************************
int getTimeFromServer(char *host, int port, char *buffer, int bufferLen) {
int sfd, sysRet, timeOut, retval;
char stringPort[8];
struct addrinfo hints, *aiList, *aiPtr = NULL;
if (strcmp(host, "") == 0) {
printf("Need hostname or IP address\n");
return(-1);
}
sprintf(stringPort, "%d", port);
memset(&hints, '\0', sizeof(hints));
//hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
sysRet = getaddrinfo(host, stringPort, &hints, &aiList);
if (sysRet != 0) {
printf("error getting network address for %s (%s)\n",
host, gai_strerror(sysRet));
exit(-1);
}
aiPtr = aiList; // search through list
while (aiPtr != 0) {
sfd = socket(aiPtr->ai_family, aiPtr->ai_socktype, aiPtr->ai_protocol);
if (sfd >= 0) {
timeOut = 100;
sysRet = 1;
while ((timeOut != 0) && (sysRet != 0)) {
sysRet = connect(sfd, aiPtr->ai_addr, aiPtr->ai_addrlen);
usleep(1000);
timeOut--;
}
if (sysRet == 0)
break; // connect successful
else
close(sfd);
}
aiPtr = aiPtr->ai_next;
}
freeaddrinfo(aiList);
if (aiPtr == NULL) {
printf("could not connect to %s\n", host);
retval = -1;
}
else
retval = 0;
if (retval == 0) {
if (write(sfd, buffer, bufferLen) < 0) {
printf("error sending request to timer serveer\n");
retval = -1;
}
else
retval = read(sfd, buffer, COM_BUF_SIZE);
}
close(sfd);
return(retval);
}
//*****************************************************************************
// Function: starts "time" socket server
// Parameter: port number to listen to
// Returns: socket file descriptor if ok, -1 if error
// Exits:
//*****************************************************************************
int StartTimeServer(int portNumber) {
int sfd, sysRet, j;
char stringPort[8];
struct addrinfo hints, *aiList, *aiPtr = NULL;
sprintf(stringPort, "%d", portNumber); // portnumber to string
memset(&hints, '\0', sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_PASSIVE;
sysRet = getaddrinfo(NULL, stringPort, &hints, &aiList);
if (sysRet != 0) {
printf("error getting network address (%s)\n", gai_strerror(sysRet));
exit(-1);
}
aiPtr = aiList; // search through list
while (aiPtr != 0) {
sfd = socket(aiPtr->ai_family, aiPtr->ai_socktype, aiPtr->ai_protocol);
if (sfd >= 0) {
j = 1;
sysRet = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j));
if (sysRet < 0)
perror("cannot set socket options");
if (bind(sfd, aiPtr->ai_addr, aiPtr->ai_addrlen) < 0) {
perror("bind failed ");
close(sfd);
exit(-1);
}
if (listen(sfd, 5) < 0) {
close(sfd);
perror("listen failed ");
exit(-1);
}
else
break;
}
aiPtr = aiPtr->ai_next;
}
freeaddrinfo(aiList);
if (aiPtr == NULL) {
printf("could not set up a socket server\n");
exit(-1);
}
return(sfd);
}
//*****************************************************************************
// Function: Reads data from client
// Parameter: socket file descriptor
// buffer to place data
// Returns: current socket descriptor on success, < 0 on failure
// Exits: none
//*****************************************************************************
int WaitForClient(int sfd, char *buffer) {
int cfd, retval, addrlen;
struct sockaddr_in addr;
addrlen = sizeof(struct sockaddr_in);
cfd = accept(sfd,(struct sockaddr *)&addr,(socklen_t *)&addrlen);
if (cfd >= 0) {
retval = read(cfd, buffer, COM_BUF_SIZE);
retval = cfd;
}
else
retval = cfd;
return(retval);
}
//*****************************************************************************
@@ -0,0 +1,20 @@
//*****************************************************************************
// ipCom.c IP Socket Functions
// Original Autor: M. Thaler, M. Pellaton (Modul BSY)
//*****************************************************************************
#ifndef IP_COM_SOCKETS
#define IP_COM_SOCKETS
#define COM_BUF_SIZE 512
#define PIDperror()\
fprintf(stderr,"fatal error, daemon with PID %d: ",getpid());
int getTimeFromServer(char *host, int port, char *buffer, int bufferLen);
int StartTimeServer(int PortNumber);
int WaitForClient(int sfd, char *buffer);
#endif
//***************************************************************************
@@ -0,0 +1,44 @@
/*********************************************************************
* File: MrTimeDaemon.c
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: einen Daemon-Prozess erzeugen
*********************************************************************/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Daemonizer.h"
#include "TimeDaemon.h"
#define STRING_LENGTH 128
//--------------------------------------------------------------------
int main(void) {
pid_t pid;
int status;
const char *lockfilePath = "/tmp/timeDaemon.lock";
//const char *lockfilePath = NULL;
const char *logfilePath = "/tmp/timeDaemon.log";
const char *livingPath = "/tmp";
const char *myName = "I am Mr. Time Daemon on \n";
if ((pid = fork()) == 0)
Daemonizer(TimeDaemon, (void *)myName,
lockfilePath, logfilePath, livingPath);
else {
assert(pid > 0);
wait(&status); // wait for Daemonizer to exit
// after having forked the "Daemon"
if (WEXITSTATUS(status) != 0)
printf("*** Daemonizer failed ***\n");
}
}
//--------------------------------------------------------------------
@@ -0,0 +1,21 @@
/*********************************************************************
* File: PlapperMaul.c
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: plappern
*********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//--------------------------------------------------------------------
int main(void) {
while (1) {
printf("Hallo, ich bins.... Pidi %d\n", getpid());
usleep(500000);
}
}
//--------------------------------------------------------------------
@@ -0,0 +1,75 @@
/******************************************************************************
* File: TimeDaemon.c
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: the daemon code
******************************************************************************/
//*****************************************************************************
// system includes
//*****************************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/un.h>
#include <netdb.h>
#include <time.h>
//*****************************************************************************
// local includes
//*****************************************************************************
#include "TimeDaemon.h"
#include "TimeDaemonDefs.h"
#include "IPsockCom.h"
//*****************************************************************************
// Function: TimeDeamon
// Parameter: data: expects here pointer to string
//*****************************************************************************
void TimeDaemon(void *data) {
TimeData tData;
char buffer[COM_BUF_SIZE];
struct tm MyTime;
time_t ActualTime;
int sfd, cfd;
printf("%s\n", (char *)data);
// start server
sfd = StartTimeServer(TIME_PORT);
if (sfd < 0) {
perror("could not start socket server");
exit(-1);
}
while (1) {
cfd = WaitForClient(sfd, buffer);
if ((strcmp(buffer, REQUEST_STRING) == 0) && (cfd >= 0)) {
time(&ActualTime);
MyTime = *localtime(&ActualTime);
tData.hours = MyTime.tm_hour;
tData.minutes = MyTime.tm_min;
tData.seconds = MyTime.tm_sec;
tData.day = MyTime.tm_mday;
tData.month = MyTime.tm_mon + 1;
tData.year = MyTime.tm_year + 1900;
gethostname(tData.servername, HOST_NAM_LEN);
write(cfd, (char *)(&tData), sizeof(tData));
}
}
// if we should somehow come here (how ?)
close(sfd);
exit(0);
}
//*****************************************************************************
@@ -0,0 +1,14 @@
/******************************************************************************
* File: TimeDaemon.h
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: function prototype of time daemon
******************************************************************************/
#ifndef TIME_DAEMON
#define TIME_DAEMON
void TimeDaemon(void *);
#endif
//*****************************************************************************
@@ -0,0 +1,30 @@
/******************************************************************************
* File: TimeDaemonDefs.h
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: Data Definitions for TimeDaemon
******************************************************************************/
#ifndef TIME_DAEMON_DEFS
#define TIME_DAEMON_DEFS
#define HOST_NAM_LEN 32
#define TIME_PORT 65534
#define REQUEST_STRING "requestTimeFromServer"
// data structure receiving from time daemon
typedef struct {
int hours;
int minutes;
int seconds;
int day;
int month;
int year;
char servername[HOST_NAM_LEN];
} TimeData, *TimeDataPtr;
#endif
//*****************************************************************************
@@ -0,0 +1,66 @@
/******************************************************************************
* File: WhatsTheTimeMr.c
* Original Autor: M. Thaler (Modul BSY)
* Aufgabe: Ask MrTimeDaemon for the time
******************************************************************************/
//*****************************************************************************
// system includes
//*****************************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/un.h>
#include <netdb.h>
#include <arpa/inet.h> // required by inet_aton
//*****************************************************************************
// local includes
//*****************************************************************************
#include "TimeDaemon.h"
#include "TimeDaemonDefs.h"
#include "IPsockCom.h"
//*****************************************************************************
// Function: main()
// Parameter: hostname or IP address in dot format
//*****************************************************************************
int main(int argc, char *argv[]) {
char buffer[COM_BUF_SIZE];
char hostname[64];
TimeDataPtr tDataPtr;
int j;
if (argc < 2) {
printf("*** no hostname or IP-address -> using localhost ***\n");
strcpy(hostname, "localhost");
} else {
strcpy(hostname, argv[1]);
}
strcpy(buffer,REQUEST_STRING);
j = getTimeFromServer(hostname, TIME_PORT, buffer, sizeof(buffer));
if (j < 0)
printf("no response from %s\n", argv[1]);
else {
tDataPtr = (TimeDataPtr)(buffer);
printf("\nIt's ");
printf("%02d:%02d:%02d",
tDataPtr->hours, tDataPtr->minutes,tDataPtr->seconds);
printf(" the %d.%d.%d on \"%s\"\n\n",
tDataPtr->day, tDataPtr->month,
tDataPtr->year, tDataPtr->servername);
}
exit(0);
}
//*****************************************************************************
@@ -0,0 +1,35 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall -g -std=gnu99
EXENAMES= MrTimeDaemon.e
EXENAMEC= WhatsTheTimeMr.e
EXENAMEP= PlapperMaul.e
LIBNAME=
compile: $(EXENAMES) $(EXENAMEC) $(EXENAMEP)
$(EXENAMES): MrTimeDaemon.o IPsockCom.o Daemonizer.o TimeDaemon.o
$(CMP) $(CMPFLAGS) MrTimeDaemon.o IPsockCom.o Daemonizer.o TimeDaemon.o $(LIBNAME) -o $(EXENAMES)
$(EXENAMEC): WhatsTheTimeMr.o IPsockCom.o
$(CMP) $(CMPFLAGS) WhatsTheTimeMr.o IPsockCom.o $(LIBNAME) -o $(EXENAMEC)
$(EXENAMEP): PlapperMaul.o
$(CMP) $(CMPFLAGS) PlapperMaul.o $(LIBNAME) -o $(EXENAMEP)
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f $(EXENAMEC) $(EXENAMES) $(EXENAMEP)
rm -f *.o
all:
@make clean
@make
@@ -0,0 +1,53 @@
//***************************************************************************
// File: ProcA1.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid;
int status;
int i;
i = 5;
printf("\n\ni vor fork: %d\n\n", i);
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
i++;
printf("\n... ich bin das Kind %d mit i=%d, ", getpid(),i);
printf("meine Eltern sind %d \n", getppid());
break;
default:
i--;
printf("\n... wir sind die Eltern %d mit i=%d ", getpid(), i);
printf("und Kind %d,\n unsere Eltern sind %d\n", pid, getppid());
wait(&status);
break;
}
printf("\n. . . . . und wer bin ich ?\n\n");
exit(0);
}
//***************************************************************************
@@ -0,0 +1,26 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall
LDFLAGS=
EXENAM1= ProcA1.e
FNAM1= ProcA1
LIBNAME=
$(EXENAM1): $(FNAM1).o
$(CMP) $(FNAM1).o $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAM1)
all:
@make clean
@make
@@ -0,0 +1,38 @@
//***************************************************************************
// File: ChildProcA3.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: arg[0]: Programmname, arg[1]: i
//***************************************************************************
int main(int argc, char *argv[]) {
int i;
if (argv[1] == NULL) {
printf("argument missing\n");
exit(-1);
}
else
i = atoi(argv[1]); // convert string argv[1] to integer i
// argv[1] is a number passed to child
printf("\n... ich bin das Kind %d mit i=%d, ", getpid(),i);
printf("meine Eltern sind %d \n", getppid());
exit(0);
}
//***************************************************************************
@@ -0,0 +1,54 @@
//***************************************************************************
// File: ProcA3.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid;
int status;
int i, retval;
char str[8];
i = 5;
printf("\n\ni vor fork: %d\n\n", i);
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
i++;
sprintf(str, "%d",i); // convert integer i to string str
retval = execl("./ChildProcA2.e", "ChildProcA2.e", str, NULL);
if (retval < 0) perror("\nexecl not successful");
break;
default:
i--;
printf("\n... wir sind die Eltern %d mit i=%d ", getpid(), i);
printf("und Kind %d,\n unsere Eltern sind %d\n", pid, getppid());
wait(&status);
break;
}
printf("\n. . . . . und wer bin ich ?\n\n");
exit(0);
}
//***************************************************************************
@@ -0,0 +1,34 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall
LDFLAGS=
EXENAME= ProcA2.e
FNAME= ProcA2
EXENAMC= ChildProcA2.e
FNAMC= ChildProcA2
LIBNAME=
LIBNAME=
compile: $(EXENAME) $(EXENAMC)
$(EXENAME): $(FNAME).o
$(CMP) $(FNAME).o $(LIBNAME) $(LDFLAGS) -o $@
$(EXENAMC): $(FNAMC).o
$(CMP) $(FNAMC).o $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAME) $(EXENAMC)
all:
@make clean
@make
@@ -0,0 +1,31 @@
//***************************************************************************
// File: ProcA4.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
fork();
fork();
fork();
fork();
printf("PID: %d\t PPID: %d\n", getpid(), getppid());
sleep(10); // keep processes in system to display their "stammbaum"
exit(0);
}
//***************************************************************************
@@ -0,0 +1,27 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall
LDFLAGS=
EXENAMES= ProcA3.e
FNAME= ProcA3
LIBNAME=
$(EXENAMES): $(FNAME).o
$(CMP) $(FNAME).o $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAMES)
all:
@make clean
@make
@@ -0,0 +1,61 @@
//***************************************************************************
// File: ProcA5.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "workerUtils.h"
#include "selectCPU.h"
#define ITERATIONS 20
#define WORK_HARD 2000000
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid;
int i;
pid = fork();
selectCPU(0); // select CPU 0
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
for (i = 0; i < ITERATIONS; i++) {
justWork(WORK_HARD);
printf("%d \t\tChild\n", i);
fflush(stdout); // force output
}
break;
default:
for (i = 0; i < ITERATIONS; i++) {;
justWork(WORK_HARD);
printf("%d \tMother\n", i);
fflush(stdout); // force output
}
}
printf("I go it ...\n");
if (pid > 0) // wait for child to terminate
waitpid(pid, NULL, 0);
exit(0);
}
//***************************************************************************
@@ -0,0 +1,2 @@
setCPU() does not work on OSX
@@ -0,0 +1,27 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall
LDFLAGS=
EXENAM1= ProcA4.e
FNAM1= ProcA4.o workerUtils.o selectCPU.o
LIBNAME=
$(EXENAM1): $(FNAM1)
$(CMP) $(FNAM1) $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAM1)
all:
@make clean
@make
@@ -0,0 +1,41 @@
//***************************************************************************
// File: setCPU.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux
#include <sched.h>
#endif
//******************************************************************************
#ifdef __linux
void selectCPU(unsigned int n) {
cpu_set_t cpuset;
if (n >= sysconf(_SC_NPROCESSORS_ONLN)) {
printf("CPU %d not availble\n", n);
exit(0);
}
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
CPU_ZERO(&cpuset);
CPU_SET(n, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
#endif
#ifdef __APPLE__
void selectCPU(unsigned int n) {
printf("Cannot set single CPU on OSX\n ... continue anyway");
}
#endif
//******************************************************************************
@@ -0,0 +1,15 @@
//*******************************************************************
// File: selectCPU.h
// Original Author: M. Thaler (Modul BSY)
//*******************************************************************
#ifndef SET_CPUS_HEADER_FILE
#define SET_CPUS_HEADER_FILE
//*******************************************************************
void selectCPU(unsigned int n);
//*******************************************************************
#endif
@@ -0,0 +1,90 @@
//*******************************************************************
// File: workerUtils.c
// Original Author: M. Thaler (Modul BSY)
// Purpose: helper applications to consume cpu time
//*******************************************************************
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include "workerUtils.h"
//------------------------------------------------------------------------------
#define MAX_CPUS 16
//------------------------------------------------------------------------------
pid_t worker[MAX_CPUS];
int nCPUS;
//------------------------------------------------------------------------------
void launchWorkLoad() {
int i;
nCPUS = sysconf(_SC_NPROCESSORS_ONLN); // get number of available CPUs
nCPUS = (nCPUS > MAX_CPUS) ? MAX_CPUS : nCPUS;
for (i = 0; i < nCPUS; i++) // start workers -> random load
worker[i] = startWorker();
}
void stopWorkLoad() {
int i;
for (i = 0; i < nCPUS; i++) // stop worker
stopWorker(worker[i]);
}
void setRandom(void) {
srandom((unsigned int)time(NULL)); // new random sequence
}
void justWork(unsigned int load) {
unsigned int j;
for (j = 0; j < load; j++) {}; // just work
}
void workHard(unsigned int low, unsigned int high) {
double rv;
unsigned int us, j;
high = high - low;
rv = ((double)random())/RAND_MAX; // make random value (0..1)
us = low + (unsigned int)(rv * high); // between lower & higher limit
for (j = 0; j < us; j++) {}; // just work
setRandom(); // reseed random generator
}
void randomSleep(unsigned int low, unsigned int high) {
double rv;
unsigned int us;
high = high - low;
rv = ((double)random())/RAND_MAX; // make random value (0..1)
us = low + (unsigned int)(rv * high); // between lower & higher limit
usleep(us*1000);
}
pid_t startWorker(void) { // make a hard working process
struct timeval tv; // limit run time to 60 secs
time_t st;
pid_t pid = fork();
if (pid == 0) { // child: pid = 0 -> ifinite loop
gettimeofday(&tv, NULL); // take start time
st = tv.tv_sec;
while ((tv.tv_sec-st) < 60) {
workHard(50, 800); // work hard (random interval
gettimeofday(&tv, NULL); // between 50us and 800us)
}
}
if (pid < 0) { // exit fork failed
printf("forking worker failed\n");
exit(0);
}
return pid; // return pid if parent
}
void stopWorker(pid_t worker) {
kill(worker, SIGKILL); // terminate worker process
}
@@ -0,0 +1,33 @@
//*******************************************************************
// File: workerUtils.h
// Original Author: M. Thaler (Modul BSY)
// Purpose: helper applications to consume cpu time
//*******************************************************************
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
//------------------------------------------------------------------------------
#define MAX_CPUS 16
//------------------------------------------------------------------------------
void launchWorkLoad();
void stopWorkLoad();
void setRandom(void);
void justWork(unsigned int load);
void workHard(unsigned int low, unsigned int high);
void randomSleep(unsigned int low, unsigned int high);
pid_t startWorker(void);
void stopWorker(pid_t worker);
@@ -0,0 +1,51 @@
//***************************************************************************
// File: ProcA6.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid, id;
char buf[64];
int i;
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
printf("\n... ich bin das Kind %d\n", getpid());
for (i = 0; i < 10; i++) {
usleep(500000); // slow down a bit
printf("Mein Elternprozess ist %d\n", id = getppid());
}
printf("... so das wars\n");
break;
default:
sleep(2); // terminate
exit(0);
break;
}
printf("\n\n*** and here my new parent ****\n\n");
sprintf(buf, "ps -p %d", id);
system(buf);
exit(0);
}
//***************************************************************************
@@ -0,0 +1,3 @@
Reparenting -> new parent instead init (1):
command prctl(PR_SET_CHILD_SUBREAPER, 1)
@@ -0,0 +1,26 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall
LDFLAGS=
EXENAMES= ProcA5.e
FNAME= ProcA5
LIBNAME=
$(EXENAMES): $(FNAME).o
$(CMP) $(FNAME).o $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAMES)
all:
@make clean
@make
@@ -0,0 +1,48 @@
//***************************************************************************
// File: ProcA7.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid;
int j;
for (j = 0; j < 3; j++) { // generate 3 processes
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
sleep(j+2); // process j sleeps for j+2 sec
exit(0); // then exits
break;
default: // parent
break;
}
}
sleep(8); // parent process sleeps for 6 sec
wait(NULL); // consult manual for "wait"
sleep(2);
wait(NULL);
sleep(2);
wait(NULL);
sleep(2);
exit(0);
}
@@ -0,0 +1,27 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall
LDFLAGS=
EXENAMES= aaaa.e
FNAME= ProcA6
LIBNAME=
$(EXENAMES): $(FNAME).o
$(CMP) $(FNAME).o $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAMES)
all:
@make clean
@make
@@ -0,0 +1,19 @@
#!/bin/bash
# M. Thaler, InIT/ZHAW 11/2014
# Version v.fs20
if test "$1" = ""
then
cmd="-a"
else
cmd="-C $1"
fi
forever="1"
while test "$forever" = "1"
do
clear
ps $cmd
sleep 1
done
@@ -0,0 +1,50 @@
//***************************************************************************
// File: ChildProcA8.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
//***************************************************************************
// Function: main(), parameter: arg[0]: Programmname, arg[1]: i
//***************************************************************************
int main(int argc, char *argv[]) {
int i = 0, *a = NULL;
if (argc > 1)
i = atoi(argv[1]); // convert string argv[1] to integer i
// argv[1] is a number passed to child
printf("\n*** I am the child having job nr. %d ***\n\n", i);
switch(i) {
case 0: exit(0); // exit normally
break;
case 1: *a = i; // force segmentation error
break;
case 2: kill(getpid(), 30); // I send signal 30 to myself
break;
case 3: sleep(5); // sleep and wait for signal
break;
case 4: sleep(5); // just sleep
exit(222); // then exit
break;
default:
exit(-1);
}
exit(0);
}
//***************************************************************************
@@ -0,0 +1,65 @@
//***************************************************************************
// File: ProcA8.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(int argc, char *argv[]) {
pid_t pid;
int status, retval, whatToDo = 0;
char str[8];
if (argc > 1)
whatToDo = atoi(argv[1]); // get job number for child
pid = fork(); // fork child
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
sprintf(str, "%d",whatToDo);
retval = execl("./ChildProcA7.e", "ChildProcA7.e", str, NULL);
if (retval < 0) perror("\nexecl not successful");
break;
default:
if (whatToDo <= 3) {
if (whatToDo == 3) {
sleep(1);
kill(pid, SIGABRT); // send signal SIGABTR to child
}
wait(&status);
if (WIFEXITED(status))
printf("Child exits with status %d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status)) {
printf("Child exits on signal %d\n", WTERMSIG(status));
printf("Child exits with core dump %d\n", WCOREDUMP(status));
}
} else {
usleep(500*1000);
while (!waitpid(pid, &status, WNOHANG)) {
printf(". . . child is playing\n");
usleep(500*1000);
}
printf("Child has exited with 'exit(%d)'\n", WEXITSTATUS(status));
}
break;
}
exit(0);
}
//***************************************************************************
@@ -0,0 +1,21 @@
Enable core dumps -> ulimit -c unlimited
Disable core dumps -> ulimit -c 0
Das core File lesen mit gdb:
gdb -c core ChildProgA8.e
!!!! Wichtig !!!!
Wenn sie ein Linux benutzen, das standardmaessig ABRT (Automatic Bug
Reporting Tool) verwendet (z.B. Fedora, Centos, etc.), muss
/proc/sys/kernel/core_pattern auf "core" gesetzt werden, damit ein
core File erzeugt wird
echo core > /proc/sys/kernel/core_pattern
Wenn ihr System SE-Linux verwendet (z.B. Fedora) , laesst sich core_pattern
nicht auf einfache Art setzen, verzichten sie in diesem Fall auf die
Erzeugung des "core" Files.
@@ -0,0 +1,34 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc
CMPFLAGS= -Wall -g
LDFLAGS=
EXENAME= ProcA7.e
FNAME= ProcA7
EXENAMC= ChildProcA7.e
FNAMC= ChildProcA7
LIBNAME=
LIBNAME=
compile: $(EXENAME) $(EXENAMC)
$(EXENAME): $(FNAME).o
$(CMP) $(FNAME).o $(LIBNAME) $(LDFLAGS) -o $@
$(EXENAMC): $(FNAMC).o
$(CMP) $(FNAMC).o $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f core *.o $(EXENAME) $(EXENAMC)
all:
@make clean
@make
@@ -0,0 +1,45 @@
//***************************************************************************
// File: ProcA9_1.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid;
printf("\n");
printf("\nHallo, I am on the way to fork now, ......lo");
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
printf("ok: I am the child\n");
break;
default:
printf("ok: I am the parent\n");
break;
}
printf("\nclear ?\n\n");
exit(0);
}
//***************************************************************************
@@ -0,0 +1,87 @@
//***************************************************************************
// File: ProcA9_2.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#define WAIT_TIME (200*1000) // 0.2 s with usleep
// globaler array
#define ARRAY_SIZE 8
char GArray[ARRAY_SIZE][ARRAY_SIZE];
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pid_t pid;
int i,j;
// flip coin to select "child first" or "parent first"
struct timeval tv;
gettimeofday(&tv, NULL);
srandom(tv.tv_usec); // evaluate seed
int head = (int)(random()) >> 7; // flip coin
head &= 0x1;
// fill global array with '-' and print array value
for (i = 0; i < ARRAY_SIZE; i++) {
for (j = 0; j < ARRAY_SIZE; j++) {
GArray[i][j] = '-';
printf("%c ", GArray[i][j]);
}
printf("\n");
}
fflush(stdout);
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0: // --- child fills upper half of array with 'c'
if (head) usleep(WAIT_TIME);
for (i = ARRAY_SIZE / 2; i < ARRAY_SIZE; i++)
for (j = 0; j < ARRAY_SIZE; j++)
GArray[i][j] = 'c';
break;
default: // --- parent fills lower half of array with 'p'
if (! head) usleep(WAIT_TIME);
for (i = 0; i < ARRAY_SIZE / 2; i++)
for (j = 0; j < ARRAY_SIZE; j++)
GArray[i][j] = 'p';
break;
}
if (pid == 0)
printf("\nKinderarray\n\n");
else
printf("\nElternarray\n\n");
for (i = 0; i < ARRAY_SIZE; i++) {
for (j = 0; j < ARRAY_SIZE; j++)
printf("%c ", GArray[i][j]);
printf("\n");
}
fflush(stdout);
if (pid > 0) wait(NULL);
exit(0);
}
//***************************************************************************
@@ -0,0 +1,74 @@
//***************************************************************************
// File: ProcA9_3.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sched.h>
#include "workerUtils.h"
#define ANZAHL 15
#define WORK_HARD 1000000
//*****************************************************************************
// Function: main(), parameter: none
//*****************************************************************************
int main(void) {
FILE *fdes;
pid_t pid;
int i;
launchWorkLoad(); // start CPU load to force context switches
fdes = fopen("AnyOutPut.txt", "w");
if (fdes == NULL) perror("Cannot open file");
usleep(500000);
pid = fork();
switch (pid) {
case -1:
perror("Could not fork");
break;
case 0:
for (i = 1; i <= ANZAHL; i++) {
fprintf(fdes, "Fritzli\t%d\n", i);
fflush(fdes); // make sure date is written to file
justWork(WORK_HARD);
}
break;
default:
for (i = 1; i <= ANZAHL; i++) {
fprintf(fdes, "Mami\t%d\n", i);
fflush(fdes); // make sure date is written to file
justWork(WORK_HARD);
}
fflush(stdout);
stopWorkLoad();
break;
}
printf("We are done\n");
if (pid > 0) {
waitpid(pid, NULL, 0);
printf("See file AnyOutPut.txt\n");
}
fflush(stdout);
exit(0);
}
//*****************************************************************************
@@ -0,0 +1,38 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc -std=gnu99
CMPFLAGS= -Wall
LDFLAGS=
EXENAME1= ProcA8_1.e
FNAM1= ProcA8_1.o
EXENAME2= ProcA8_2.e
FNAM2= ProcA8_2.o
EXENAME3= ProcA8_3.e
FNAM3= ProcA8_3.o workerUtils.o
LIBNAME=
compile: $(EXENAME1) $(EXENAME2) $(EXENAME3)
$(EXENAME1): $(FNAM1)
$(CMP) $(CMPFLAGS) $(FNAM1) $(LIBNAME) $(LDFLAGS) -o $@
$(EXENAME2): $(FNAM2)
$(CMP) $(CMPFLAGS) $(FNAM2) $(LIBNAME) $(LDFLAGS) -o $@
$(EXENAME3): $(FNAM3)
$(CMP) $(CMPFLAGS) $(FNAM3) $(LIBNAME) $(LDFLAGS) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAME1) $(EXENAME2) $(EXENAME3)
all:
@make clean
@make
@@ -0,0 +1,90 @@
//*******************************************************************
// File workerUtils.c
// Original Author: M. Thaler (Modul BSY)
// Purpose: helper applications to consume cpu time
//*******************************************************************
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include "workerUtils.h"
//------------------------------------------------------------------------------
#define MAX_CPUS 16
//------------------------------------------------------------------------------
pid_t worker[MAX_CPUS];
int nCPUS;
//------------------------------------------------------------------------------
void launchWorkLoad() {
int i;
nCPUS = sysconf(_SC_NPROCESSORS_ONLN); // get number of available CPUs
nCPUS = (nCPUS > MAX_CPUS) ? MAX_CPUS : nCPUS;
for (i = 0; i < nCPUS; i++) // start workers -> random load
worker[i] = startWorker();
}
void stopWorkLoad() {
int i;
for (i = 0; i < nCPUS; i++) // stop worker
stopWorker(worker[i]);
}
void setRandom(void) {
srandom((unsigned int)time(NULL)); // new random sequence
}
void justWork(unsigned int load) {
unsigned int j;
for (j = 0; j < load; j++) {}; // just work
}
void workHard(unsigned int low, unsigned int high) {
double rv;
unsigned int us, j;
high = high - low;
rv = ((double)random())/RAND_MAX; // make random value (0..1)
us = low + (unsigned int)(rv * high); // between lower & higher limit
for (j = 0; j < us; j++) {}; // just work
setRandom(); // reseed random generator
}
void randomSleep(unsigned int low, unsigned int high) {
double rv;
unsigned int us;
high = high - low;
rv = ((double)random())/RAND_MAX; // make random value (0..1)
us = low + (unsigned int)(rv * high); // between lower & higher limit
usleep(us*1000);
}
pid_t startWorker(void) { // make a hard working process
struct timeval tv; // limit run time to 60 secs
time_t st;
pid_t pid = fork();
if (pid == 0) { // child: pid = 0 -> ifinite loop
gettimeofday(&tv, NULL); // take start time
st = tv.tv_sec;
while ((tv.tv_sec-st) < 60) {
workHard(50, 800); // work hard (random interval
gettimeofday(&tv, NULL); // between 50us and 800us)
}
}
if (pid < 0) { // exit fork failed
printf("forking worker failed\n");
exit(0);
}
return pid; // return pid if parent
}
void stopWorker(pid_t worker) {
kill(worker, SIGKILL); // terminate worker process
}
@@ -0,0 +1,25 @@
//*******************************************************************
// File: workerUtils.h
// Original Author: M. Thaler (Modul BSY)
//*******************************************************************
#ifndef WORKER_UTILS
#define WORKER_UTILS
void launchWorkLoad();
void stopWorkLoad();
void setRandom(void);
void justWork(unsigned int load);
void workHard(unsigned int low, unsigned int high);
void randomSleep(unsigned int low, unsigned int high);
pid_t startWorker(void);
void stopWorker(pid_t worker);
#endif
@@ -0,0 +1,112 @@
//***************************************************************************
// File: ProcA9.c
// Original Author: M. Thaler (Modul BSY)
//***************************************************************************
//***************************************************************************
// system includes
//***************************************************************************
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include "selectCPU.h"
//**************************************************************************
// global data
#define ARRAY_SIZE 8
char GArray[ARRAY_SIZE][ARRAY_SIZE];
//**************************************************************************
void *ThreadF(void *letter) {
int i,j;
int LowLim, HighLim;
char letr;
letr = *(char *)letter;
if (letr == 'p') { // paremeter = p: fill lower half of array
LowLim = 0; HighLim = ARRAY_SIZE / 2;
}
else { // paremeter != p: fill upper half of array
LowLim = ARRAY_SIZE / 2; HighLim = ARRAY_SIZE;
}
for (i = LowLim; i < HighLim; i++) { // fill own half
for (j = 0; j < ARRAY_SIZE; j++)
GArray[i][j] = letr;
}
for (i = 0; i < ARRAY_SIZE; i++) { // print whole array
for (j = 0; j < ARRAY_SIZE; j++)
printf("%c ", GArray[i][j]);
printf("\n");
}
printf("\n");
fflush(stdout);
pthread_exit(0);
}
//***************************************************************************
// Function: main(), parameter: none
//***************************************************************************
int main(void) {
pthread_t thread1, thread2;
int i,j, pthr;
char letter1, letter2;
selectCPU(0); // run on CPU 0
// flip coin to select p or c first
struct timeval tv;
gettimeofday(&tv, NULL);
srandom(tv.tv_usec); // evaluate seed
int head = (int)(random()) >> 7; // flip coin
head &= 0x1;
if (head) {
letter1 = 'p';
letter2 = 'c';
}
else {
letter1 = 'c';
letter2 = 'p';
}
for (i = 0; i < ARRAY_SIZE; i++)
for (j = 0; j < ARRAY_SIZE; j++)
GArray[i][j] = '-';
printf("\nArray vor Threads\n\n");
for (i = 0; i < ARRAY_SIZE; i++) {
for (j = 0; j < ARRAY_SIZE; j++)
printf("%c ", GArray[i][j]);
printf("\n");
}
printf("\n");
pthr = pthread_create(&thread1, NULL, ThreadF, (void *)&letter1);
if (pthr != 0) perror("Could not create thread");
pthr = pthread_create(&thread2, NULL, ThreadF, (void *)&letter2);
if (pthr != 0) perror("Could not create thread");
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("\n... nach Threads\n");
for (i = 0; i < ARRAY_SIZE; i++) {
for (j = 0; j < ARRAY_SIZE; j++)
printf("%c ", GArray[i][j]);
printf("\n");
}
}
//***************************************************************************
@@ -0,0 +1,27 @@
# *************************************************************
# Original Autor: M. Thaler (Modul BSY)
# *************************************************************
CMP= gcc -std=gnu99 -pthread
CMPFLAGS= -Wall
LDFLAGS=
EXENAME1= ProcA9.e
FNAM1= ProcA9.o selectCPU.o
LIBNAME=
$(EXENAME1): $(FNAM1)
$(CMP) $(FNAM1) $(LDFLAGS) $(LIBNAME) -o $@
.c.o:
$(CMP) -c $(CMPFLAGS) $<
.cc.o:
$(CMP) -c $(CMPFLAGS) $<
clean:
rm -f *.o $(EXENAME1)
all:
@make clean
@make
@@ -0,0 +1,41 @@
//******************************************************************************
// File: setCPU.c
// Original Author: M. Thaler (Modul BSY)
//******************************************************************************
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux
#include <sched.h>
#endif
//******************************************************************************
#ifdef __linux
void selectCPU(unsigned int n) {
cpu_set_t cpuset;
if (n >= sysconf(_SC_NPROCESSORS_ONLN)) {
printf("CPU %d not availble\n", n);
exit(0);
}
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
CPU_ZERO(&cpuset);
CPU_SET(n, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
#endif
#ifdef __APPLE__
void selectCPU(unsigned int n) {
printf("Cannot set single CPU on OSX\n ... continue anyway");
}
#endif
//******************************************************************************
@@ -0,0 +1,15 @@
//******************************************************************************
// File: setCPU.h
// Original Author: M. Thaler (Modul BSY)
//******************************************************************************
#ifndef SET_CPUS_HEADER_FILE
#define SET_CPUS_HEADER_FILE
//******************************************************************************
void selectCPU(unsigned int n);
//******************************************************************************
#endif