2025-06-03 06:54:00 +00:00
|
|
|
### Erklärung der Praktikumsaufgabe (Synchronisation)
|
2025-06-03 00:50:08 +02:00
|
|
|
|
|
|
|
|
#### **Kernziel der Aufgabe**
|
|
|
|
|
Sie sollen ein **multithreaded Sensornetzwerk** simulieren, das drei Komponenten umfasst:
|
|
|
|
|
1. **Sensoren (Producer)**
|
|
|
|
|
- Erzeugen regelmäßig Messdaten (z.B. Temperatur) als Zufallszahlen.
|
|
|
|
|
- Speichern Daten in einem **gemeinsamen Ringpuffer** (Thread-sicher synchronisiert).
|
|
|
|
|
2. **Analyse-Module (Consumer & Reader)**
|
|
|
|
|
- Entnehmen Daten aus dem Ringpuffer.
|
|
|
|
|
- Lesen **gleichzeitig** ein zentrales Analysemodell (z.B. Kalibrierungsdaten).
|
|
|
|
|
3. **System-Controller (Writer)**
|
|
|
|
|
- Aktualisiert das Analysemodell in zufälligen Abständen.
|
|
|
|
|
|
|
|
|
|
#### **Technische Anforderungen**
|
|
|
|
|
1. **Synchronisationsmechanismen**
|
|
|
|
|
- Nutzen Sie **nur Semaphore und Mutex** (wie in der Vorlesung behandelt).
|
|
|
|
|
- Vermeiden Sie **Race Conditions** und **Deadlocks**.
|
|
|
|
|
2. **Ringpuffer**
|
|
|
|
|
- Vorgegebene Implementierung (nicht threadsicher!) → Sie müssen **Zugriff synchronisieren**.
|
|
|
|
|
- Eigenschaften:
|
|
|
|
|
- Größe `BUFFER_SIZE` (z.B. 8 Elemente).
|
|
|
|
|
- Überschreibt älteste Daten bei Vollschreiben (`ring_push`).
|
|
|
|
|
- Blockiert beim Lesen, wenn leer (`ring_pop`).
|
|
|
|
|
3. **Analysemodell**
|
|
|
|
|
- Einfache Implementierung als **globaler Integer** (keine komplexe Analyse).
|
|
|
|
|
- **Reader-Writer-Problem**:
|
|
|
|
|
- Mehrere Leser (Analyse-Module) dürfen **gleichzeitig** lesen.
|
|
|
|
|
- Writer (Controller) benötigt **exklusiven Zugriff** (kein Leser/Writer aktiv).
|
|
|
|
|
- **Starvation des Writers verhindern** (Controller darf nicht endlos warten).
|
|
|
|
|
4. **Simulation**
|
|
|
|
|
- Sensoren/Controller: `sleep()` + `rand()` für Intervalle.
|
|
|
|
|
- Ausgaben: Nutzen Sie `printf()` zur Beobachtung des Systems.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### **Zu implementierende Synchronisation**
|
|
|
|
|
#### 1. **Ringpuffer (Producer-Consumer Pattern)**
|
|
|
|
|
- **Problem**: Paralleler Zugriff von Sensoren (Produzenten) und Analyse-Modulen (Konsumenten).
|
|
|
|
|
- **Lösung**:
|
|
|
|
|
- **Mutex**: Schützt den Puffer bei `push`/`pop` (strukturelle Integrität).
|
|
|
|
|
- **Semaphore `items`**: Zählt belegte Pufferplätze.
|
|
|
|
|
- Initialwert: `0` (leer).
|
|
|
|
|
- **Producer** (Sensor): Erhöht `items` **nur wenn Puffer vorher nicht voll war** (sonst Überschreiben ohne Signal).
|
|
|
|
|
- **Consumer** (Analyse-Modul): Wartet auf `items > 0` vor dem Lesen.
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
// Pseudocode: Sensor (Producer)
|
|
|
|
|
pthread_mutex_lock(&buffer_mutex);
|
|
|
|
|
bool was_full = ring_is_full(&rb);
|
|
|
|
|
ring_push(&rb, new_data);
|
|
|
|
|
if (!was_full) sem_post(&items); // Signal an Consumer
|
|
|
|
|
pthread_mutex_unlock(&buffer_mutex);
|
|
|
|
|
|
|
|
|
|
// Pseudocode: Analyse-Modul (Consumer)
|
|
|
|
|
sem_wait(&items); // Wartet auf Daten
|
|
|
|
|
pthread_mutex_lock(&buffer_mutex);
|
|
|
|
|
ring_pop(&rb, &data);
|
|
|
|
|
pthread_mutex_unlock(&buffer_mutex);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 2. **Analysemodell (Reader-Writer Problem mit Fairness)**
|
|
|
|
|
- **Problem**: Paralleles Lesen vs. exklusives Schreiben + Verhinderung von Writer-Starvation.
|
|
|
|
|
- **Lösung** (Fairness mit "Turnstile"-Semaphor):
|
|
|
|
|
- **Semaphore `turn`**: Garantiert FIFO-Reihenfolge (Fairness).
|
|
|
|
|
- **Semaphore `rw_mutex`**: Schützt exklusive Schreibzugriffe.
|
|
|
|
|
- **Mutex `read_mutex`**: Schützt Leser-Zähler (`read_count`).
|
|
|
|
|
- **Integer `read_count`**: Zählt aktive Leser.
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
// Pseudocode: Analyse-Modul (Reader)
|
|
|
|
|
sem_wait(&turn); // Stelle dich in Warteschlange
|
|
|
|
|
sem_post(&turn); // Weiterleitung (sofort)
|
|
|
|
|
pthread_mutex_lock(&read_mutex);
|
|
|
|
|
read_count++;
|
|
|
|
|
if (read_count == 1) sem_wait(&rw_mutex); // Erster Leser sperrt Writer
|
|
|
|
|
pthread_mutex_unlock(&read_mutex);
|
|
|
|
|
|
|
|
|
|
// LESEZUGRIFF auf shared_model
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&read_mutex);
|
|
|
|
|
read_count--;
|
|
|
|
|
if (read_count == 0) sem_post(&rw_mutex); // Letzter Leser erlaubt Writer
|
|
|
|
|
pthread_mutex_unlock(&read_mutex);
|
|
|
|
|
|
|
|
|
|
// Pseudocode: Controller (Writer)
|
|
|
|
|
sem_wait(&turn); // Warte auf deine Reihenfolge
|
|
|
|
|
sem_wait(&rw_mutex); // Fordere exklusiven Zugriff an
|
|
|
|
|
sem_post(&turn); // Freigabe für Nächsten in Warteschlange
|
|
|
|
|
|
|
|
|
|
// SCHREIBZUGRIFF auf shared_model
|
|
|
|
|
|
|
|
|
|
sem_post(&rw_mutex); // Freigabe
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### **Test und Reflexion**
|
|
|
|
|
Starten Sie das System mit verschiedenen Parametern und beobachten Sie:
|
|
|
|
|
1. **Durchsatzrate**
|
|
|
|
|
- Wie viele Daten werden pro Zeiteinheit verarbeitet?
|
|
|
|
|
- Variieren Sie: Puffergröße, Anzahl der Sensoren/Analyse-Module.
|
|
|
|
|
2. **Starvation/Deadlocks**
|
|
|
|
|
- Tritt Starvation auf (z.B. Controller wird blockiert)?
|
|
|
|
|
- Verursachen bestimmte Konfigurationen Deadlocks?
|
|
|
|
|
3. **Lastszenarien**
|
|
|
|
|
- **Viele Sensoren**: Läuft der Puffer über? (Datenverlust durch Überschreiben)
|
|
|
|
|
- **Viele Leser**: Wird der Controller blockiert? Wie wirkt sich Fairness aus?
|
|
|
|
|
4. **Systemunterbrechungen**
|
|
|
|
|
- Simulieren Sie OTA-Updates: Controller führt `sleep()` während Schreibzugriff aus → Blockiert Leser?
|
|
|
|
|
5. **Optimale Parameter**
|
|
|
|
|
- Finden Sie Konfigurationen, bei denen das System besonders effizient ist.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### **Umsetzungshinweise**
|
|
|
|
|
- **Globale Variablen**:
|
|
|
|
|
```c
|
|
|
|
|
RingBuffer rb; // Ringpuffer
|
|
|
|
|
int shared_model = 0; // Analysemodell (Integer)
|
|
|
|
|
sem_t items, rw_mutex, turn; // Semaphore
|
|
|
|
|
pthread_mutex_t buffer_mutex, read_mutex; // Mutexe
|
|
|
|
|
int read_count = 0; // Zähler aktiver Leser
|
|
|
|
|
```
|
|
|
|
|
- **Thread-Erstellung**:
|
|
|
|
|
```c
|
|
|
|
|
// Beispiel: Starte 2 Sensoren, 3 Analyse-Module, 1 Controller
|
|
|
|
|
pthread_t sensor_threads[2], analyser_threads[3], controller_thread;
|
|
|
|
|
for (int i = 0; i < 2; i++) pthread_create(&sensor_threads[i], NULL, sensor_func, NULL);
|
|
|
|
|
for (int i = 0; i < 3; i++) pthread_create(&analyser_threads[i], NULL, analyser_func, NULL);
|
|
|
|
|
pthread_create(&controller_thread, NULL, controller_func, NULL);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- **Wichtig**: Initialisieren Sie alle Synchronisationsmittel vor Thread-Start!
|
|
|
|
|
```c
|
|
|
|
|
sem_init(&items, 0, 0); // Initialwert 0
|
|
|
|
|
sem_init(&rw_mutex, 0, 1); // Initialwert 1 (frei)
|
|
|
|
|
sem_init(&turn, 0, 1); // Initialwert 1 (FIFO-Reihenfolge)
|
|
|
|
|
pthread_mutex_init(&buffer_mutex, NULL);
|
|
|
|
|
pthread_mutex_init(&read_mutex, NULL);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### **Warum diese Lösung?**
|
|
|
|
|
- **Ringpuffer**: Produzenten überschreiben alte Daten, Konsumenten warten bei Leerpuffer → Effizient & verlusttolerant.
|
|
|
|
|
- **Analysemodell**:
|
|
|
|
|
- `turn`-Semaphor verhindert Writer-Starvation (Fairness).
|
|
|
|
|
- Leser können parallel arbeiten, solange kein Writer aktiv ist.
|
|
|
|
|
- **Synchronisationsmittel**: Beschränkt auf Semaphore/Mutex (vorgabekonform).
|
|
|
|
|
|
|
|
|
|
Mit dieser Struktur erfüllen Sie alle Lernziele:
|
|
|
|
|
✅ Reader-Writer-Problem
|
|
|
|
|
✅ Producer-Consumer-Pattern
|
2025-06-03 06:54:00 +00:00
|
|
|
✅ Vermeidung von Race Conditions & Deadlocks!
|