Blog 7 Min. Lesezeit

Analyse der FreeRTOS-Architektur mit CppDepend

Diesen Artikel teilen
Analyse der FreeRTOS-Architektur mit CppDepend

Eine statische Codeanalyse mit CppDepend liefert tiefe architektonische Einblicke in die strukturellen Abhängigkeiten der FreeRTOS-Codebasis. Mithilfe einer Dependency Structure Matrix (DSM) können Entwickler visualisieren, wie die zentralen Kernel-Komponenten miteinander interagieren.

DSM mit dem bidirektionalen Abhängigkeitszyklus zwischen timers.c und tasks.c

Zentrale architektonische Erkenntnisse

  • Identifizierte Abhängigkeitszyklen: Die Analyse hebt explizit eine zirkuläre Abhängigkeit zwischen Kernmodulen hervor: Source/timers.c bildet einen Abhängigkeitszyklus mit Source/tasks.c.
  • DSM-Mapping: Die Dependency Structure Matrix visualisiert dateiübergreifende Kopplungen zwischen zentralen Komponenten wie Source/event_groups.c, Source/list.c, Source/queue.c, Source/tasks.c und Source/timers.c.
  • Aufschlüsselung der Verzeichnisstruktur: Das Projektlayout erfasst Komponenten unter dem Haupt-Namespace freertos, einschließlich Source/*, Source/include/* und zielspezifischer Source/portable/-Implementierungen neben externen Abhängigkeiten.

Vertiefung: Der Abhängigkeitszyklus zwischen timers.c und tasks.c

Beim Drill-down in die Dependency Structure Matrix (DSM) offenbart die Farbcodierung die genaue Natur dieser Kopplung:

  • Die rote Zelle: Ein Doppelklick auf die rote Zelle hebt den übergreifenden bidirektionalen Abhängigkeitszyklus zwischen Source/timers.c und Source/tasks.c hervor.
  • Die grünen Zellen: Stellen direkte Abhängigkeiten dar, die von Funktionen und Strukturen in tasks.c in Richtung timers.c fließen.
  • Die blauen Zellen: Stellen die umgekehrten Abhängigkeiten dar, die von timers.c zurück nach tasks.c fließen – etwa Referenzen auf zentrale Task-Steuerungen, Scheduler-Zustände und gemeinsame Strukturen.

CppDepend Dependency Structure Matrix der FreeRTOS-Codebasis

Lässt sich die Architektur verbessern?

Aus rein softwaretechnischer Sicht ist eine vollständige Entkopplung (z. B. Timer als völlig unabhängiges Plug-in-Modul) technisch möglich, bringt jedoch erhebliche Kompromisse für eingebettete Systeme mit sich:

  • Performance & Overhead: FreeRTOS ist bewusst für Mikrocontroller mit strengen RAM- und CPU-Zyklen-Beschränkungen konzipiert. Eine Entkopplung erfordert typischerweise Abstraktionsschichten, Funktionszeigertabellen oder dynamische Registrierungs-Hooks. Das erhöht RAM-Verbrauch, ROM-Footprint und Aufruflatenz.
  • Monolithisches Echtzeit-Design: In sicherheitskritischem oder ressourcenbeschränktem RTOS-Design werden eng kontrollierte, vorhersehbare Interaktionen zwischen Kern-Scheduler und Zusatzdiensten (wie Timern) gegenüber losen Plugin-Architekturen bevorzugt, um deterministisches Verhalten und einfachere statische Analyse (etwa MISRA-Konformität) zu gewährleisten.

Wenn Sie die Codegröße minimieren oder anpassen möchten, was kompiliert wird, erlaubt FreeRTOS den vollständigen Ausschluss von timers.c aus Ihrer CMakeLists.txt, sofern Ihre Anwendung keine Software-Timer nutzt – übrig bleiben nur tasks.c, queue.c und list.c für einen möglichst schlanken Footprint.

Geringe Kopplung der FreeRTOS-Funktionen

Die Analyse der Methodenkopplung mit Code Quest (CQLinq) zeigt, dass nahezu alle FreeRTOS-Funktionen eine außergewöhnlich geringe Kopplung aufweisen.

Code-Quest-Abfrage: nur 4 FreeRTOS-Methoden rufen mehr als 10 andere Methoden auf

Die Abfrage von Methoden, deren Anzahl aufgerufener Methoden (NbMethodsCalled) größer als 10 ist, liefert eine bemerkenswert kleine Treffermenge:

from m in Methods
where m.NbMethodsCalled > 10
select new { m, m.NbMethodsCalled }
  • Minimale Hochkopplungs-Ausreißer: In der gesamten freertos-Lösung erreichen nur 4 Methoden diesen Schwellenwert oder überschreiten ihn.
  • Zentrale Queue-Operationen: Die einzigen Funktionen jenseits dieser Grenze liegen in Source/queue.c unter __Globals – konkret zentrale Routing- und Synchronisationsprimitiven wie xQueueGenericSend, xQueueReceive, xQueueSemaphoreTake und xQueuePeek (13 bis 14 aufgerufene Methoden).
  • Architektonische Erkenntnis: Dies bestätigt, dass die überwiegende Mehrheit der FreeRTOS-Funktionen granular, in sich geschlossen ist und mit einer streng kontrollierten Zahl von Partnern interagiert.

Granulare Modularität: Funktionsarchitektur in Code City

Die Visualisierung der FreeRTOS-Codebasis als 3D-Code-City bietet eine intuitive Perspektive auf die Strukturierung der Funktionen. Statt ausgedehnter monolithischer Codeblöcke zeigt das architektonische Layout eine dichte Stadt kompakter, hochspezialisierter Gebäude, die einzelne Funktionen repräsentieren.

Die FreeRTOS-Codebasis als 3D-Code-City in CppDepend

  • Kompakter Funktionsumfang: Code-City-Metriken bestätigen, dass zentrale Routinen einen schlanken, beherrschbaren Umfang behalten. Kritische Speicherverwaltungsroutinen wie pvPortMalloc(size_t) umfassen etwa nur 71 Codezeilen und erreichen dennoch ein A-Rating für Wartbarkeit.
  • Single-Responsibility-Prinzip: Die einheitlichen Gebäudegrößen in der Stadt unterstreichen die Designphilosophie von FreeRTOS – jede Funktion ist auf einen klar umrissenen Zweck isoliert.
  • Geringe Komplexität und hohe Wartbarkeit: Durch kleine Routinen werden komplexe Bedingungslogik und technische Schulden minimiert – entscheidend für Echtzeitsysteme.

Hohe Wartbarkeit der FreeRTOS-Quelldateien

Die Analyse des Architektur-Abhängigkeits- und Metrikgraphen zeigt, dass FreeRTOS über alle zentralen Quelldateien und Module hinweg eine außergewöhnliche Wartbarkeit aufweist.

Code Map von timers.c: Blöcke nach LOC dimensioniert und nach Wartbarkeit eingefärbt

  • Einheitliche „A“-Bewertungen: Jede wichtige Subsystemkomponente – einschließlich Source/queue.c, Source/tasks.c, Source/timers.c und Source/portable/MemMang/heap_4.c – erhält ein Top-A-Rating für Wartbarkeit.
  • Null technische Schulden: Trotz kritischer RTOS-Operationen verzeichnen Dateien wie Source/queue.c (624 LOC) und Source/tasks.c (1036 LOC) 0 min 0 s technische Schulden – ein Beleg für saubere Einhaltung von Coding-Standards.
  • Hoher Maintainability Index (MI): Der Wartbarkeitsindex bleibt dateiübergreifend konstant hoch – von komfortablen Werten in heap_4.c (MI: 66) bis zu exzellenten Bewertungen in Source/timers.c (MI: 76) und portablen Helper-Headern (MI: 78).

Code-Map-Einblick: Ein Blick in timers.c

Die Betrachtung einer einzelnen Datei wie timers.c in der Code-Map-Metrikvisualisierung (Blöcke nach Codezeilen dimensioniert und nach Wartbarkeit eingefärbt) liefert einen granularen Einblick, wie einzelne Routinen zur Sauberkeit der Datei beitragen:

FreeRTOS-Abhängigkeitsgraph in CppDepend

  • Konsistente grüne Färbung: Die überwiegende Mehrheit der Funktionen in timers.c ist in sattem Grün eingefärbt – ein Zeichen für hohen Wartbarkeitsindex und geringe strukturelle Komplexität.
  • Ausgewogene Methodengrößen: Größere Utility-Blöcke wie prvProcessReceivedCommands nehmen zwar einen spürbaren Teil des Layouts ein, sind aber sauber neben kleineren Hilfsfunktionen partitioniert.
  • Saubere architektonische Verteilung: Diese visuelle Aufschlüsselung bestätigt, dass Funktionen selbst in Dateien mit komplexer Echtzeit-Synchronisationslogik kompakt, klar definiert und hochgradig wartbar bleiben.

Hier einige zusätzliche übergeordnete FreeRTOS-Design- und Architekturmuster:

1. Die Portabilitätsschicht (saubere Trennung der Zuständigkeiten)

  • Hardware-Abstraktion: FreeRTOS isoliert generische Scheduling-Logik sauber von prozessorspezifischen Details.
  • Die portable/-Grenze: Sämtlicher hardwareabhängiger Code – Kontextwechsel, Interrupt-Behandlung, SysTick-Konfiguration – ist im Verzeichnis Source/portable/ gekapselt.
  • Architektonischer Nutzen: Dieses modulare Design hält den Kernel-Kern (tasks.c, queue.c) zu 100 % portabel über Dutzende Architekturen (ARM Cortex-M, RISC-V usw.).

2. Opaque Pointer und Datenkapselung

  • Information Hiding: FreeRTOS nutzt intensiv opaque Zeiger und Handle-Typen (wie TaskHandle_t oder QueueHandle_t), statt rohe Kontrollblockstrukturen offenzulegen.
  • Architektonischer Nutzen: Anwendungscode interagiert mit dem Kernel ausschließlich über klar definierte APIs. Das verhindert enge Kopplung zwischen Benutzer-Tasks und internen Kernel-Zuständen.

3. Plug-and-Play-Speicherverwaltungsstrategien

  • Mehrere Heap-Optionen: FreeRTOS bietet fünf unterschiedliche Speicherverwaltungs-Implementierungen (heap_1.c bis heap_5.c) – von einfachen Allokatoren ohne Freigabe bis zu fortschrittlichen Coalescing-Algorithmen.
  • Architektonischer Nutzen: Embedded-Entwickler erhalten architektonische Kontrolle über das Speicherverhalten. Für sicherheitskritische Systeme (etwa Automotive oder Luftfahrt) lassen sich deterministische, fragmentierungsfreie Allokatoren wählen.

4. Geringe zyklomatische Komplexität für Echtzeit-Determinismus

  • Begrenzte Ausführungspfade: Weil einzelne Funktionen klein sind und geringe Verzweigungskomplexität aufweisen (weniger verschachtelte if-else-Anweisungen und Schleifen), bleibt die Ausführungszeit von Kernel-Routinen hochgradig vorhersehbar.
  • Architektonischer Nutzen: In harten Echtzeit-Betriebssystemen (RTOS) ist vorhersehbare Ausführungszeit (Determinismus) ebenso wichtig wie Korrektheit. Geringe Komplexität garantiert, dass der Scheduler strikte Timing-Deadlines einhält.

Fazit: Lehren aus der FreeRTOS-Analyse

Die Architekturanalyse mit CppDepend liefert klare Einblicke, wie FreeRTOS Echtzeit-Anforderungen mit Clean-Code-Praktiken in Einklang bringt:

  • Pragmatische Kopplung statt absoluter Entkopplung: Zwischen Source/timers.c und Source/tasks.c existiert zwar ein Abhängigkeitszyklus durch geteilten Zustand wie xTimerTaskHandle – doch diese Designentscheidung minimiert Abstraktions-Overhead, Funktionszeiger-Indirektion und Speicherbedarf: kritische Prioritäten für ressourcenbeschränkte Mikrocontroller.
  • Außergewöhnliche Modularität & geringe Kopplung: Abgesehen von dieser einen engen Interaktion weist die Codebasis bemerkenswert geringe Kopplung auf. Nur vier Funktionen im gesamten Repository rufen mehr als zehn andere Methoden auf – Komplexität bleibt strikt auf zentrale Queue- und Synchronisationsprimitiven in Source/queue.c beschränkt.
  • Hohe Wartbarkeit & null technische Schulden: Durch granulare Single-Responsibility-Funktionen erreicht FreeRTOS Top-A-Wartbarkeitsratings und 0 s technische Schulden in Schlüsseldateien wie tasks.c, timers.c und heap_4.c.

Möchten Sie die Architektur Ihrer eigenen Firmware sehen? Laden Sie CppDepend herunter und analysieren Sie Ihre Codebasis in wenigen Minuten. Lesen Sie auch, wie wir ein anderes Projekt visualisiert haben: Vom KI-Code zur lebendigen Code City.

Diesen Artikel teilen