RTOS-Konzepte Teil 1 · Lernplattform
Echtzeit- und Betriebssysteme · Teil 1

RTOS-Konzepte —
interaktive Lernplattform

Threads, Semaphore, Mutex und Counting Semaphore — anschaulich erklärt mit Analogien aus dem Alltag, animierten Visualisierungen, erklärtem C++-Code, einem Fachwortglossar (Englisch → Deutsch) und einem selbstkorrigierenden Quiz. Alles, um die Konzepte wirklich zu verstehen und zu behalten.

↺ Wiederholung starten Zu den Konzepten → ✓ Quiz & Fragen
01
Threads / Tasks
Multitasking & Scheduler
02
Binäre Semaphore
Signalisierung
03
Mutex
Exklusiver Zugriff
04
Counting Semaphore
Ressourcen-Pool
Rückblick · zum Aufklappen

Wiederholung — worum ging es bisher?

Bevor es in die Konzepte geht: die wichtigsten Grundlagen aus den ersten Terminen kompakt. Klapp die Abschnitte auf, um dein Wissen aufzufrischen.

T1 Betriebssystem vs. Echtzeitbetriebssystem aufklappen
Allgemeines Betriebssystem
  • Steuert Programmausführung, verwaltet CPU, RAM, Dateisysteme
  • Abstraktionsschicht + Ressourcenmanager
  • Ziel: hoher Datendurchsatz, Multitasking, flüssige Bedienung
  • „Faire" Verteilung der Ressourcen
Echtzeitbetriebssystem (RTOS)
  • Höchste Zuverlässigkeit & Determinismus (Vorhersehbarkeit)
  • Strikte Einhaltung von Fristen (Hard Real-Time)
  • Strikt prioritätsbasiertes Scheduling statt Fairness
  • Minimale, vorhersehbare Latenzen
T2 Scheduling & Task-Lebenszyklus aufklappen

Der Scheduler bestimmt, welcher Task gerade die CPU bekommt — ein Kern kann nicht mehrere Befehle exakt gleichzeitig ausführen.

kooperativ
Tasks geben die CPU freiwillig ab.
präemptiv
Das OS kann Tasks die CPU aktiv entziehen.
Zustände eines Tasks
Neu Bereit Laufend Wartend Suspendiert Beendet

Ticks sind der Herzschlag des OS: ein periodischer Timer-Interrupt macht die Zeitsteuerung effizient und deterministisch — gerechnet wird in Ticks statt echten Zeiteinheiten.

T3 Simulator RTOS-EDU & Tracing aufklappen

Die Simulationsumgebung vereint vier Bausteine:

FreeRTOS
Echtzeit-Kernel
Pigweed
Middleware-Abstraktion (pw_*)
LVGL
Grafik für das GUI
Wayland
Display-Server-Protokoll
tasks_trace → Binärdatei → decode_trace.py → JSON → Perfetto

In Perfetto (ui.perfetto.dev) lassen sich Task-Wechsel, CPU-Auslastung, Sleep-Phasen und Signalzustände visuell auf einer Zeitachse analysieren.

Konzepte eines Echtzeitbetriebssystems

Von der nativen API zur Abstraktion

Dieselben Konzepte gibt es einmal als native FreeRTOS-Funktion und einmal als portable Pigweed-Abstraktion (pw_*). Die Tabelle zeigt beide nebeneinander.

Konzept FreeRTOS (native) Pigweed (pw_*) Beschreibung
Threads / TasksxTaskCreate()pw_thread::ThreadFassadenbasiertes Threading mit portablen Entry-Points
Binäre SemaphorexSemaphoreCreateBinary()pw_sync::BinarySemaphoreStandard-Schnittstelle zur Signalisierung zwischen Threads/ISRs
MutexxSemaphoreCreateMutex()pw_sync::MutexRAII-Pattern (z. B. mit std::lock_guard)
Counting SemaphorexSemaphoreCreateCounting()pw_sync::CountingSemaphoreAbstrahiert die Zähler-Logik fürs Ressourcen-Management
Queues / MessagingxQueueSend() / xQueueReceive()pw_channel / pw_ring_bufferChannels für Datenströme statt generischer Queues
Timeouts / DelayvTaskDelay()pw_chrono::SystemClockTypsichere Zeitangaben (ms vs. ticks) à la std::chrono
InterruptsxSemaphoreGiveFromISR()pw_interruptVereinheitlicht das Handling von Interrupt-Contexten
Software TimerxTimerCreate()pw_chrono::SystemTimerObjektorientierte Callbacks nach Zeitablauf

Teil 1 behandelt: Threads/Tasks · Binäre Semaphore · Mutex · Counting Semaphore

01
Konzept 01

Threads / Tasks

🎮
Alltags-Analogie · Modernes PC-Spiel

Ein Thread fragt die Tastatur ab (T1), ein zweiter berechnet die Physik der Welt (T2), ein dritter lädt im Hintergrund neue Texturen von der Festplatte (T3). Ohne Threads würde das Spiel einfrieren, sobald man ein neues Level betritt — weil eine einzige Aufgabe alles blockieren würde.

T1 · Tastatur T2 · Physik T3 · Texturen
Interaktive Visualisierung
Implementierung · erklärt
task_1_loop() — die Task-Funktion
static void task_1_loop() {
  bool on = false;
  while (true) {
    int period_ms = slider1.get_value();
    on = !on;
    if (on) led1.on(0x0d, 0x6e, 0xfd); // blau
    else    led1.off();

    // Task blockiert für period_ms → gibt CPU frei
    pw::this_thread::sleep_for(
      pw::chrono::SystemClock::for_at_least(
        std::chrono::milliseconds(period_ms)));
  }
}
while(true) — die Endlosschleife macht daraus eine dauerhaft laufende Task (LED blinkt regelmäßig).
sleep_for(...) versetzt die Task in den blockierten Zustand — in dieser Zeit belegt sie keine CPU, andere Tasks laufen.
Erzeugt wird die Instanz im main() mit DetachedThread(options, ...) — Name, Priorität und Stack-Kontext werden über ThreadOptions gesetzt.
main() — Task-Instanz erzeugen & Scheduler starten
const platform::ThreadOptions options =
    platform::ThreadOptions()
        .set_name("Task_1")
        .set_priority(1)
        .set_static_context(ctx_task_1);

pw::thread::DetachedThread(options, [] { task_1_loop(); });
vTaskStartScheduler();  // ab hier verteilt der Scheduler die CPU-Zeit
02
Konzept 02

Binäre Semaphore

🏁
Alltags-Analogie · Startschuss beim 100 m-Lauf

Die Läufer (Tasks) warten blockiert an der Linie. Sobald der Schiedsrichter die Pistole abfeuert (Signal geben), dürfen sie losrennen. Es geht nicht um den Schutz einer Ressource, sondern um das Warten auf ein Ereignis. Ein Producer signalisiert, ein Consumer wartet.

Interaktive Visualisierung
Implementierung · erklärt
Producer — gibt das Signal
static void producer_loop() {
  while (true) {
    sleep_for(slider_period.get_value());

    // Signal an Consumer senden
    semaphore.release();

    led_producer.on(0x0d,0x6e,0xfd); // blau
    sleep_for(100ms);
    led_producer.off();
  }
}
Consumer — wartet auf das Signal
static void consumer_loop() {
  while (true) {
    // blockiert, bis release() kommt
    semaphore.acquire();

    // Signal empfangen → LED an
    led_consumer.on(0x1a,0xc2,0x55); // grün
    sleep_for(150ms);
    led_consumer.off();
  }
}
release() (give) erhöht die Semaphore auf 1 — das ist der „Startschuss". Läuft periodisch.
acquire() (take) blockiert bei 0 und läuft erst weiter, wenn der Producer signalisiert — keine CPU-Last beim Warten.
Prioritäten: Producer läuft mit Priorität 2, Consumer mit 1. So kann der höher-priore Producer den Consumer bei Bedarf verdrängen (präemptiv).
03
Konzept 03 · Mutual Exclusion

Mutex

🔑
Alltags-Analogie · Der Toilettenschlüssel im Café

Es gibt nur einen Schlüssel. Wer ihn hat, darf rein — alle anderen warten vor der Tür. Erst wenn der Vorgänger den Schlüssel zurückgibt, kann der Nächste die Ressource (Toilette) nutzen. Das verhindert, dass zwei Leute gleichzeitig denselben Raum nutzen und Chaos entsteht. Besitzprinzip: nur wer sperrt, gibt frei.

Interaktive Visualisierung
Implementierung · erklärt
task1_loop() — kritischer Abschnitt
static void task1_loop() {
  while (true) {
    sleep_for(slider1.get_value());
    {
      // Mutex sperren (RAII)
      std::lock_guard lock(mutex);

      led1.on(0x0d,0x6e,0xfd);
      shared_counter = (shared_counter + 1) % 201;
      counter_bar.set_value(shared_counter);
      sleep_for(80ms); // Arbeit im Abschnitt
      led1.off();
    } // Mutex hier automatisch frei
  }
}
std::lock_guard lock(mutex) sperrt beim Betreten des Blocks. Task_2 muss warten, bis Task_1 fertig ist.
RAII: Beim Verlassen der { }-Klammern wird der Mutex automatisch freigegeben — auch bei einem Fehler. Kein vergessenes unlock().
% 201 begrenzt den Zähler zyklisch: er läuft im Kreis 0…200 und überschreitet nie 200.
Task_2 ist identisch aufgebaut (orange LED) und teilt sich denselben mutex und shared_counter. Ohne Mutex käme es beim gleichzeitigen counter+1 zu einer Race Condition — mit Mutex zählt der geteilte Zähler sauber hoch.
04
Konzept 04

Counting Semaphore

🚲
Alltags-Analogie · Leihfahrrad-Stand mit 10 Rädern

Jeder Student, der ein Rad nimmt, verringert den Zähler. Sind alle 10 Räder weg (Zähler = 0), muss der Nächste warten. Sobald jemand ein Rad zurückbringt, wird der Zähler erhöht und die nächste wartende Person darf losfahren. Die Semaphore verwaltet also einen Pool identischer Ressourcen.

Interaktive Visualisierung
Implementierung · erklärt
Producer — Slot freigeben (release)
static void producer_loop() {
  while (true) {
    sleep_for(slider_produce.get_value());

    if (available_slots.load() < kSlots) {
      semaphore.release();
      int slots = available_slots.fetch_add(1)+1;
      slots_bar.set_value(slots);
      led_producer.on(0x0d,0x6e,0xfd);
    }
  }
}
Consumer — Slot belegen (acquire)
static void consumer_loop() {
  while (true) {
    // blockiert, bis ≥ 1 Slot frei
    semaphore.acquire();

    int slots = available_slots.fetch_sub(1)-1;
    slots_bar.set_value(slots);
    led_consumer.on(0x1a,0xc2,0x55);
    sleep_for(slider_consume.get_value());
  }
}
Start bei 0: Die Semaphore beginnt mit 0 freien Slots — der Consumer muss also zuerst auf den Producer warten. Maximum ist kSlots = 8.
std::atomic<int> schützt den Anzeige-Zähler vor Race Conditions. fetch_sub(1) zieht 1 ab, liefert aber den alten Wert zurück — daher -1 für den aktuellen.
Zusammenfassung

Mutex vs. Binäre vs. Counting Semaphore

Die drei Synchronisationsmittel im direkten Vergleich — Zustände, Grundkonzept und Einsatzzweck.

Merkmal 🔑 Mutex 🏁 Binäre Semaphore 🚲 Counting Semaphore
Zustände / Werte Gesperrt oder Entsperrt Binär: 0 (nicht verfügbar) oder 1 (verfügbar) Zähler: 0 bis zu einem definierten Maximum
Grundkonzept Besitztum — nur der sperrende Thread darf freigeben Signalisierung — ein Task/Interrupt gibt das Signal, ein anderer wartet Ressourcen-Zählung — verwaltet einen Pool identischer Ressourcen
Einsatzzweck Exklusiver Zugriff auf eine gemeinsame Ressource (serielle Schnittstelle, globale Variable, I²C-Bus) Synchronisation von Tasks oder Aufwecken aus einer ISR Mengenverwaltung (Puffer-Füllstand, freie Plätze, gleichzeitige Tasks begrenzen)
Merksatz Mutex
„Ein Schlüssel, ein Besitzer." Schützt eine Ressource.
Merksatz Binär
„Startschuss." Signalisiert ein Ereignis.
Merksatz Counting
„Fahrradstand." Zählt mehrere Plätze.
Bonus · Semaphore im Überblick
📈
Analyse
Tracing & Ablaufverfolgung mit Perfetto

Um das zeitliche Verhalten der Tasks sichtbar zu machen, zeichnet der Simulator Ereignisse zur Laufzeit auf. So erkennt man genau, wann ein Task auf einen Mutex oder eine Semaphore warten muss.

1 · Aufzeichnen
tasks_trace schreibt Systemereignisse per Knopfdruck in eine Binärdatei.
2 · Decodieren
decode_trace.py erzeugt mit der ELF-Datei eine JSON-Datei.
3 · Analysieren
In ui.perfetto.dev: Task-Wechsel, CPU-Last, Sleep-Phasen & Signalzustände auf der Zeitachse.
Fachwortglossar

Begriffe — Englisch → Deutsch, erklärt

Jeder Fachbegriff mit englischer Bezeichnung, deutscher Übersetzung und einer verständlichen Erklärung. Filtere nach Kategorie oder such nach einem Stichwort.

{{ glossaryCount }}
{{ g.term }}
{{ g.tag }}
EN{{ g.en }} DE{{ g.de }}
{{ g.def }}
Keine Begriffe gefunden. Andere Suche oder Kategorie „Alle" wählen.
Quiz · selbstkorrigierend

Teste dein Wissen

Wähle bei jeder Frage eine Antwort und klicke „Antwort prüfen". Du bekommst sofort richtig/falsch mit einer Erklärung.

{{ scoreText }}
{{ progressText }}
{{ q.num }}
{{ q.q }}
{{ q.resultLabel }}
{{ q.exp }}
Offene Fragen · zum Selbst-Beantworten

Erkläre es in eigenen Worten

Schreibe zuerst deine eigene Antwort ins Feld — dann klapp die Musterlösung auf und vergleiche. So merkst du dir die Konzepte am besten.

01
Worin unterscheidet sich ein RTOS von einem allgemeinen Betriebssystem?
Musterlösung anzeigen
Ein allgemeines Betriebssystem optimiert auf Datendurchsatz, Multitasking und flüssige Bedienung mit „fairer" Ressourcenverteilung. Ein RTOS ist auf Determinismus (Vorhersehbarkeit), höchste Zuverlässigkeit und die strikte Einhaltung zeitlicher Fristen (Hard Real-Time) spezialisiert. Statt Fairness nutzt es strikt prioritätsbasiertes Scheduling, um Latenzen minimal und vorhersehbar zu halten.
02
Beschreibe den Lebenszyklus eines Tasks mit seinen Zuständen.
Musterlösung anzeigen
Ein Task durchläuft: Neu (Initialisierung) → Bereit (wartet in der Ready Queue auf CPU) → Laufend (aktive Ausführung) → Wartend (blockiert, wartet auf ein I/O-Ereignis) und kann Suspendiert oder Beendet werden. Der Scheduler bewegt Tasks zwischen Bereit und Laufend.
03
Warum belegt ein blockierter (wartender) Task keine CPU?
Musterlösung anzeigen
Ein blockierter Task wartet auf ein Ereignis (Signal, Ressource oder Zeit) und wird vom Scheduler übersprungen. Er pollt nicht aktiv, sondern wird erst wieder bereit, wenn das Ereignis eintritt (z. B. eine Semaphore freigegeben wird). So bleibt CPU-Zeit für andere, lauffähige Tasks frei.
04
Erkläre den Unterschied zwischen Mutex und binärer Semaphore.
Musterlösung anzeigen
Beide können den Wert 0/1 haben, aber der Zweck unterscheidet sich: Der Mutex dient dem exklusiven Zugriff auf eine Ressource nach dem Besitzprinzip — nur der sperrende Thread darf wieder freigeben. Die binäre Semaphore dient der Signalisierung: ein beliebiger Task oder eine ISR gibt das Signal, ein anderer wartet darauf. Kein Besitz.
05
Was ist eine Race Condition? Erkläre sie am Beispiel counter++.
Musterlösung anzeigen
Eine Race Condition entsteht durch ungeschützten, gleichzeitigen Zugriff mehrerer Threads. counter++ ist auf CPU-Ebene drei Schritte: (1) Wert aus dem RAM in ein Register laden, (2) im Register um 1 erhöhen, (3) zurück in den RAM schreiben. Unterbricht ein zweiter Thread zwischen diesen Schritten, arbeiten beide mit dem alten Wert — eine Erhöhung geht verloren. Abhilfe: std::atomic oder ein Mutex.
06
Wozu dient das RAII-Pattern mit std::lock_guard?
Musterlösung anzeigen
RAII (Resource Acquisition Is Initialization) bindet eine Ressource an die Lebensdauer eines Objekts. std::lock_guard sperrt den Mutex im Konstruktor und gibt ihn beim Verlassen des Gültigkeitsbereichs im Destruktor automatisch wieder frei — selbst bei einem vorzeitigen return oder einer Exception. So kann man das Entsperren nicht vergessen.
07
Wie funktioniert die Trace-Analyse im RTOS-EDU-Simulator (Schritte)?
Musterlösung anzeigen
1) Aufzeichnen: tasks_trace schreibt Systemereignisse zur Laufzeit per Knopfdruck in eine Binärdatei. 2) Decodieren: decode_trace.py wandelt die Rohdaten mithilfe der kompilierten ELF-Datei in JSON um. 3) Analysieren: Die JSON-Datei wird in Perfetto (ui.perfetto.dev) geladen — dort sieht man Task-Wechsel, CPU-Auslastung, Sleep-Phasen und Signalzustände auf einer Zeitachse.
08
Nenne für Counting Semaphore ein Alltagsbeispiel und erkläre, was der Zähler bedeutet.
Musterlösung anzeigen
Beispiel: ein Leihfahrrad-Stand mit 10 Rädern. Der Zähler steht für die Anzahl verfügbarer Ressourcen (freie Räder bzw. Slots). Jeder acquire() nimmt eine Ressource und verringert den Zähler; bei 0 muss gewartet werden. Jeder release() gibt eine zurück und erhöht den Zähler. So begrenzt die Semaphore die Zahl gleichzeitiger Nutzer.
RTOS-Konzepte Teil 1 · interaktive Lernplattform
Threads · Binäre Semaphore · Mutex · Counting Semaphore
↑ Nach oben