/* ### Erklärung der Praktikumsaufgabe (Synchronisation) #### **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 ✅ Vermeidung von Race Conditions & Deadlocks! */