(Embedded) Software muss verschiedene zentrale Aufgaben softwareweit koordinieren. Das klassische Beispiel dafür ist die Initialisierung, die auf allen Ebenen der Software stattfinden muss. Bei genauerer Betrachtung lassen sich produktabhängig viele weitere dieser softwareweiten Aktionen identifizieren.
Bild 1: Prinzip des Manager Patterns
(Bild: Microconsult - Th. Batt)
Dieser Beitrag [1] stellt für das Management dieser Aufgaben in der Software das Manager Pattern vor. Es ist hochgradig skalierbar und somit anwendbar für sehr einfache Software, aber auch für komplexeste Architekturen. Das Pattern ist unabhängig von der Programmiersprache und unterstützt sowohl die prozedurale als auch objektorientierte Softwareentwicklung. Um diesen Beitrag vollständig zu verstehen, sind Kenntnisse der UML [2], der prozeduralen und objektorientierten Programmierung in C und C++ [8] von Vorteil.
1. Zentrale Aufgabe in der Software
Zur Instanziierung und Initialisierung von Objekten in der Software sind bereits Design Patterns wie „Abstract Factory“ und „Builder“ als „Creational Patterns“ von Erich Gama et.al. [3] publiziert. Des Weiteren ist im Internet [4] das „Manager Design Pattern“, auch bekannt als „Manager-Managed Design Pattern“ oder „Managed Object Design Pattern“ veröffentlicht.
Das hier vorgestellte Embedded Software Manager Pattern verantwortet alle in der Software verteilten und zentral zu koordinierenden Aufgaben. Die Anzahl und Vielfalt der Aufgaben ist abhängig von den System-/ Softwareanforderungen, die das Produkt erfüllen muss. Dieser Beitrag betrachtet die folgenden Aufgaben:
(Bild: MicroConsult - Th. Batt)
2. Prinzip des Embedded Software Manager Patterns
In einer sehr einfachen Software besteht das Manager Pattern lediglich aus einer SoftwareManager Klasse oder einem SoftwareManager Modul, welches die zuvor genannten Aufgaben übernimmt. Mit wachsender Softwarearchitektur erhalten die Architekturelemente (AE) auf jeder Architekturebene (architectureLevel = x) ihre eigenen AEnManager, die die Aufgaben dezentral für ihr Architekturelement übernehmen.
Die verschiedenen Architekturelemente sind durch Namen <<type>> typisierbar (z.B. Software-Layer, Software-Subsystem). Bei tieferen Architekturhierarchien sind häufig keine individuellen Typennamen mehr identifizierbar. Aus diesem Grund sind die Architekturhierarchien hier nicht mit Typennamen versehen, sondern der architectureLevel ist angegeben.
Somit ist das Manager Pattern für beliebig hierarchisch und beliebig flach wachsende Architekturen anwendbar. Die Manager sind fähig, untereinander zu kommunizieren.
3. Skalierbarkeit des Embedded Software Manager Patterns
Ein Architekturelement AE1.1 kann theoretisch eine unbegrenzte Anzahl 0…z weiterer Architekturelemente auf der gleichen Ebene enthalten, die wiederum mit Managern ausgestattet sind.
Die Hierarchiestufen-Anzahl der Architekturelement ist theoretisch ebenfalls unbegrenzt von 1…x oder 1…y. Je nach Architekturzweig sind unterschiedliche Hierarchiestufen-Anzahlen möglich.
Unabhängig von der UML-Notation lassen sich die Architekturebenen und Anzahlen der Architekturelemente in den einzelnen Eben sehr gut in einem Baumdiagramm darstellen. Die Astknoten bilden die Managerklassen/ -module ab.
Die unterste Architektureben 0 ist main() zugeordnet, während die Ebene 1 für den übergeordneten SoftwareManager reserviert ist. Danach beginnen die eigentlichen Architekturebenen 2…x mit ihren AEnManagern.
4. Initialisierung (prozeduraler Ansatz)
Im prozeduralen Umfeld kommen Managermodule zum Einsatz, symbolisiert durch das „m“ vor den Namen. Im Architekturelement AEn ist der mAEnManager für seine Module m1...mN verantwortlich.
main () ruft init () vom mSoftwareManager auf. Diese Funktion ruft wiederum von allen mAEnManagern die initAEn () Funktionen in der erforderlichen Reihenfolge auf (Delegationen). Diese Funktionen initialisieren die Module aus deren Verantwortlichkeit. Das Prinzip der Delegation ist für die meisten weiteren Managerfunktionen anwendbar.
Im Vergleich zu einem objektorientierten Ansatz ist beim prozeduralen häufig keine Objektinstanziierung erforderlich. Somit könnten ggf. die create () Funktionen entfallen. Die aus der objektorientierten Programmierung bekannten Konstruktoren und Destruktoren von Klassen lassen sich in der prozeduralen Programmierung durch manuell aufzurufende Funktionen construct () und destruct () ersetzen.
5. Objektinstanziierung und Initialisierung (objektorientierter Ansatz)
main () instanziiert ein cSoftwareManager Ur-Objekt. Dieses instanziiert alle cAEnManager Objekte, die wiederum alle Objekte der Klassen c1…cN aus deren Verantwortung instanziieren. Abhängig von den angewandten Relationen (Assoziation, Aggregation) erzeugen die createX () Funktionen deren Objekte dynamisch und implementierungsabhängig auf dem Heap. Bei der durchgängig angewandten Komposition werden alle Objekte automatisch auf dem Stack angelegt, wobei die createX () Funktionen entfallen.
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel Communications Group GmbH & Co. KG, Max-Planckstr. 7-9, 97082 Würzburg einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von redaktionellen Newslettern nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung, Abschnitt Redaktionelle Newsletter.
Die initX () Funktion erlaubt zusätzlich zum Konstruktor-Aufruf weitere Initialisierungen. Als Alternative ruft der Konstruktor die jeweilige initX () Funktion auf.
Ist die Architekturhierarchie tiefer als die hier dargestellte, erstreckt sich der oben beschriebene Ablauf über die gesamte Hierarchie.
Als Ergebnis dieses Schrittes sind alle Objekte instanziiert und grundinitialisiert.
6. Initialisierung der Relationen
Die Funktion buildXRelations () initialisiert die Relationen (Assoziation, Aggregation) zwischen den Objekten intern, aber auch über Architekturelement-Grenzen hinweg. Dazu gehören auch die Callback-Registrierungen. Typischerweise sind Zeiger mit entsprechender Objektadresse initialisiert.
Häufig sind dazu Objektadressen aus nebenliegenden Architekturelementen erforderlich. Kennen sich diese AEnManager untereinander, so können sie Objektadressen übermitteln.
Nach der komplett abgeschlossenen Initialisierung detektiert die Software z.B., auf welcher Hardware die Ausführung stattfindet. Ergebnisabhängig liest der SoftwareManager die korrespondierenden Parameter aus einer lokalen oder in der Cloud befindlichen Datenbank.
Die Funktion configX () der AEnManager gibt die Parameter zu den zu konfigurierenden Klassen/ Modulen weiter. Eventuell ist configX () bereits vor initX () ausführbar und gibt die Parameter über die initX () Funktion weiter.
8. Allokation von Ressourcen
Die einzelnen Architekturelemente benötigen ggf. für den weiteren Betrieb zusätzliche individuelle Ressourcen. Besonders in einer funktional-sicherheitskritischen Embedded-Software ist es empfehlenswert, die erforderlichen Ressourcen bereits zu Beginn der Softwareausführung komplett anzulegen. Damit ist zum einen sichergestellt, dass alle Ressourcen zur Laufzeit verfügbar sind, zum anderen erfolgt dadurch gleichzeitig eine Laufzeit-Performanceverbesserung.
Ressourcen sind z.B. Speicher oder bei Anwendung eines Betriebssystems bereits Betriebsmittel wie Mailboxen, Eventgruppen, Semaphore, Mutexe und Timer. Bedingung wäre, dass das Betriebssystem vor dem Bootvorgang dies bereits erlaubt. Falls nein, lässt sich die Reihenfolge der Funktionsaufrufe ändern oder der Funktionsaufruf allocateXResources () in die Architekturelement-spezifischen taskInit () verschieben.
9. Betriebsphase: Ohne Betriebssystem
Ein Bare-Metal-Applikation führt eine oder mehrere Funktionen kontinuierlich aus. Hier enthält die run () Funktion diese zentrale while (true) loop und führt in vorgegebener Reihenfolge kontinuierlich die runAEn () Funktionen aus.
10. Boot-, Startup- und Betriebsphase: Mit Betriebssystem
Die start () Funktion des SoftwareMangers bootet das Betriebssystem. Anschließend schedult das Betriebssystem als erste Task die taskInit (). Diese kreiert taskOperation () und alle taskAEnInit () Tasks der AEnManager. Anschließend beendet sie sich selbst. Die taskAEnInit () kreiert taskARnOperation () und ggf. weitere Architekturelement-spezifische Tasks. Anschließend beendet sie sich ebenfalls. Nun beginnt die eigentliche Betriebsphase mit dem Scheduling der verbleibenden Tasks.
Die Manager-Taskfunktionen taskXOperation () enthalten einen Zustandsfolgeautomaten, der den aktuellen Zustand der Software bzw. der einzelnen Architekturelemente repräsentiert. Dieser Zustandsautomat ist im weiteren Verlauf des Kapitels vorgestellt.
11. Diagnose
Die Funktion executeDiagnostics () des SoftwareManagers startet die Diagnoseausführung und sammelt alle Diagnosedaten über die Aufrufe der in den AEnManagern enthaltenen executeAEnDiagnostics () Funktionen.
Das Element Diagnostics speichert bzw. ermöglicht die Ausgabe der gespeicherten Daten.
12. Neustart
Die Funktion restart () des SoftwareManagers löst den Software-Neustart aus, indem die Aufrufe der in den AEnManagern enthaltenen restartAEn () Funktionen die einzelnen Architekturelemente neu aufstarten.
Zur feineren Unterteilung des Neustarts, aber auch des nachfolgend erklärten Herunterfahrens, dienen die gegenläufigen Funktionen zu denen, die die Manager während des Hochlaufs ausführen.
(Bild: MicroConsult - Th. Batt)
13. Herunterfahren
Die Funktion shutdown () des SoftwareManagers löst das Herunterfahren der Software aus, indem die Aufrufe der in den AEnManagern enthaltenen shutdownAEn () Funktionen die einzelnen Architekturelemente herunterfahren.
14. Zentrale Fehlerbehandlung
Die Klassen c1…cN bzw. Module m1...mN erkennen ihre Fehler und melden diese ihrem AEnManager. Dieser leitet die Fehler direkt an den übergeordneten SoftwareManager weiter. Der SoftwareManager übernimmt zentral die Fehlerbehandlung, auch für potentiell auftretende Fehler in den AEnManagern.
Neben der richtigen Fehlerreaktion, z.B. mittels handleError () und recoverError (), übernimmt der SoftwareManager auch den Fehlereintrag in sein ErrorLogbook.
15. Dezentrale Fehlerbehandlung
Die Klassen c1…cN bzw. Module m1...mN erkennen ihre Fehler und melden diese ihrem AEnManager. Kann der AEnManager den Fehler bearbeiten, tut er dies und trägt den Fehler in sein lokales ErrorLogbook ein.
Fatale Fehler, die der AEnManager nicht selbst bearbeiten kann, leitet er an den übergeordneten SoftwareManager weiter. Dieser übernimmt die Fatal-Fehlerbehandlung und trägt diese in sein ErrorLogbook ein.
16. Manager Zustandsfolge-Automat
Der SoftwareManager Zustandsfolge-Automat repräsentiert die möglichen Zustände der gesamten Software bzw. des Systems. Der AEnManager Zustandsfolge-Automat repräsentiert die möglichen Zustände des einzelnen Architekturelements.
17. Manager Ausführungsfluss
Als Zwischenzusammenfassung ist hier der Ausführungsablauf der einzelnen Funktionen auf Ebene des SoftwareManagers dargestellt. Dahinter verbergen sich die delegierenden Aufrufe der AEnManager. Dies ist nur eine Ablaufvariante von vielen möglichen.
18. Diskussionspunkte und Verbesserungen
Das bisher vorgestellte Manager Pattern lässt sich durch weitere Design- und Implementierungsdetails, aber auch Varianten verändern und ergänzen. Im Folgenden werden hierzu ein detaillierteres Grundkonzept und ein detaillierteres erweitertes Konzept vorgestellt.
19. Detailliertes Grundkonzept mit Fehlernotifikation
Im Common::Management sind das Fehler-Notifikationsinterface icbcErrorHandler (icbc == Interface Callback Class) und weitere Klassen für Diagnose und Fehlerbehandlung enthalten. Diese Elemente werden gleichermaßen in allen Managern verwendet.
Das Applikationselement cN notifiziert über die notifyError () Funktion des Interfaces icbcErrorHandler die Fehler an den cAEnManager. Der cAEnManager wiederum notifiziert seine Fehler über das gleiche Interface an den cSoftwareManager.
Bei Anwendung der Komposition erfolgt die Objektinstanziierung als eingebettete Elemente/ Attribute:
class cSoftwareManager : public Common::Management::icbcErrorHandler{ //… private: //… tcDiagnostics<mMaxNumberOfDiagnosticsData> mDiagnostics; cErrorLogbook mErrorLogbook; cAEnManager mAEnManager;};
Die Aufrufe der delegierten Funktionen müssen hier einzeln erfolgen, da keine gemeinsamen Typen definiert sind:
Der exemplarische C++ Programmcode des detaillierten Grundkonzepts ist im Download [1] enthalten.
20. Detailliertes erweitertes Konzept mit Fehlernotifikation und Manager-Interface
Bei Anwendung der Assoziation/ Aggregation erfolgt die Objektinstanziierung dynamisch und der Zugriff über Zeiger:
class cSoftwareManager : public Common::Management::icbcErrorHandler{ //… private: //… icDiagnostics* mPtrDiagnostics; icErrorLogbook* mPtrErrorLogbook; std::array<icAEnManager*, mMaxNumberOfAEnManager> mAEnManagerContainer;};
Die Aufrufe der delegierten Funktionen erfolgen in einer Schleife (einfach erweiterbar), da alle cAEnManager den gemeinsame Interfacetype icAEnManager (ic == Interface Class) implementieren und der cSoftwareManager über ein Zeigerarray darauf zugreift [5-7]:
void cSoftwareManager::create(void){ mPtrDiagnostics = new cDiagnostics{ mObjectID_Diagnostics, this }; mPtrErrorLogbook = new cErrorLogbook{ mObjectID_ErrorLogbook, this };mAEnManagerContainer[mAEnManagerID] = new AEn::cAEnManager{mObjectID_AEnManager}; // add other AEnManager objects here // for all AEnManager objects call ... for (auto&& refElement : mAEnManagerContainer) { if (refElement != nullptr) { refElement->create(); } }}
Der exemplarische C++ Programmcode des detaillierten erweiterten Konzepts ist im Download [1] enthalten.
21. Resümee
In der Praxis muss jede Embedded-Software die hier vorgestellten verteilten und zentral zu koordinierenden Aufgaben ausführen. Anforderungsabhängig sind weniger oder mehr dieser Aufgabenart auszuführen. Um die konzeptionelle Integrität auch über mehrere Projekte zu wahren, eignet sich der Einsatz von Softwarepatterns. Wie bei jedem Pattern muss der Softwarearchitekt, der Softwareentwickler bzw. das Softwareteam die hier exemplarisch vorgestellten Strukturen und Abläufe auf die individuellen Gegebenheiten in der Software anpassen.
[8] MicroConsult-Trainings – auch Live-Online: Requirements Engineering und Management für Embedded-Systeme Software-Architekturen für Embedded- und Echtzeitsysteme Embedded C++: Objektorientierte Programmierung für Mikrocontroller mit C++/EC++ Embedded C++ für Fortgeschrittene: Objektorientierte Programmierung für Mikrocontroller mit C++/EC++ Embedded-Software-Design und Patterns mit C
Thomas Batt ist zertifizierter Trainer und Coach bei MicroConsult.
(Bild: MicroConsult)
Dipl.-Ing. (FH) Thomas Batt ist gebürtiger Freiburger. Nach seiner Ausbildung als Radio- und Fernsehtechniker studierte er Nachrichtentechnik in Offenburg. Seit 1994 arbeitet er kontinuierlich in verschiedenen Branchen und Rollen im Bereich Embedded-/Real-Time Systementwicklung. 1999 wechselte Thomas Batt zu MicroConsult, heutige MicroConsult Academy. Dort verantwortet er als zertifizierter Trainer und Coach die Themenbereiche Systems-/Software Engineering für Embedded-/Real-Time-Systeme sowie Entwicklungsprozess-Beratung. (sg)