Skip to main content

Das Software Architektur Design definiert, wie alles ineinander wirkt

Heute stellt sich oftmals das Architektur Design als mächtigster Mechanismus im Software Engineering dar, um Software Qualitätsattribute wie Wiederverwendbarkeit, Verstehbarkeit, Wartbarkeit, Robustheit, Testbarkeit ... zu verbessern.

Architektur Design zielt auf sehr viele Gesichtspunkte eines Systems und dessen strukturellen Aufbau hinsichtlich dieser Gesichtspunkte. Häufig wird das Architektur Design als statische Aufteilung des Systems in logische Komponenten gesehen. Das ist aber nur ein Bereich von vielen.

Andere Bereiche sind die Darstellung und Strukturierung von Versionen (historische Entwicklung), Varianten und deren Unterschiede zueinander, verschiedene Betriebsmodi oder auch die Struktur von Daten.

Letztendlich gibt es noch den Blickwinkel auf das dynamische Verhalten, auch Laufzeit Architektur genannt. Dieses ist in ablauforientierten Systemen mit hohen Zeitanforderungen die Basis für Wartbarkeit, Erweiterbarkeit, Wiederverwendbarkeit ...

Trotzdem wird diesem Teil des Architektur Designs meist wenig Beachtung geschenkt. In dieser Newsletter erfahren Sie mehr.

Architektur Design

Die Geburt dieser Newsletter ist eine der Schwierigsten. Sie dauert nun über ein Jahr und ich habe im Verlauf der Entstehung bereits 2 andere Newsletter Ausgaben geschrieben und veröffentlicht.

Architektur Design scheint in vielerlei Hinsicht ein schwieriges Thema zu sein. Es gibt ja bereits eine ältere Ausgabe der Newsletter zu diesem Thema, und sie ist die am wenigsten von unserer Website geladene Ausgabe. Und das, obwohl ich seit Jahren der festen Überzeugung bin, dass im Architektur Design das größte Potential für Effizienzsteigerung der meisten heutigen Embedded Applikationen liegt. Warum stößt dann diese Newsletter auf so geringes Interesse? Ein Phänomen über das ich schon lange grüble.

Architektur

Im Rahmen der Softwareentwicklung repräsentiert die Softwarearchitektur die früheste Softwaredesign-Entscheidung (Architekturentwurf). Sie wird wesentlich durch Softwarequalitätskriterien, also nicht-funktionale Eigenschaften wie Modifizierbarkeit, Wartbarkeit, Sicherheit oder Performance bestimmt. Eine einmal eingerichtete Softwarearchitektur ist später nur mit hohem Aufwand abänderbar. Die Entscheidung über ihr Design ist somit eine der kritischsten und wichtigsten Punkte im Entwicklungsprozess einer Software.

Aktuell im Jahr 2011 wirbt die OOP (einer der wichtigsten und größten Kongresse im Bereich Software Engineering) speziell mit dem Thema Software Architektur Design. Es werden Software Engineering Gurus zitiert, die Architektur Design als stark vernachlässigtes Thema halten, obwohl es als Dreh- und Angelpunkt für die wesentlichen Qualitätsmerkmale heutiger Software Systeme gilt.

Auf der Website des Software Engineering Institut (SEI) steht zu diesem Thema:


„Die Architektur ist der primäre Träger der System Qualitäten, wie Leistung, Modifizierbarkeit und Sicherheit, von denen keine ohne eine zusammenführende architektonische Vision erreicht werden kann“

Quelle: http://www.sei.cmu.edu/architecture/

Aber wenn sich die Experten scheinbar einig sind, warum ist Architektur Design trotzdem das meist vernachlässigte Thema im Bereich Software Engineering? Warum wird in der Praxis ein großer Bogen um dieses Thema gemacht?

Nach vielen Recherchen zur Vorbereitung der Inhalte dieser Ausgabe bekomme ich eine Ahnung: Architektur Design ist äußerst abstrakt. Begrifflichkeiten sind nur wenig exakt definiert bzw. in der Praxis etabliert. Allein auf der Homepage des SEI stehen über 200 Definitionen, die sich zum Teil sogar widersprechen.

Und warum tue ich mich selbst so schwer mit dieser Newsletter? Vielleicht weil meine eigenen Erfahrungen und Meinungen diesbezüglich häufig in Konfrontation zur einschlägigen Literatur oder zu Lehrmeinungen stehen. Das lässt mich zweifeln ob meine eigenen Kenntnisse überhaupt fundiert sind. Auf der anderen Seite beschäftige ich mich seit ca. 15 Jahren intensiv mit diesem Thema. In der Praxis konnte ich in meinen Architektur Workshops immer wieder pragmatisch helfen die Architekturen unserer Kunden auf einen soliden Sockel zu stellen. Es muss doch etwas richtig sein an meinen Ansätzen. Also wage ich einen neuen Anlauf, in der Hoffnung etwas mehr Licht ins Dunkel zu bringen.

Eine Unterscheidung, die selten gemacht wird

Ich persönlich halte es für sehr wichtig, das Architektur Design in zwei Gesichtspunkte zu trennen.

  1. Applikations Sicht (Struktur aus Sicht der Applikation)
  2. Software Engineering Sicht (Architekturmuster, Architekturstil, Baumaterial)

Was meine ich damit ? Betrachten wir einen Hochbau Architekten, dann würde ich sagen seine Aufgabe ist es ein Haus entsprechend den Anforderungen des Bauherrn zu planen. Hier sind der kreativen Freiheit jedoch Rahmenbedingungen gesetzt, z.B. in Form von Bebauungsrichtlinien, Normen oder Art und Beschaffenheit von Halbzeugen.

So wird ein guter Architekt Maße immer im vielfachen von Ziegelgrößen verwenden, wenn das Haus herkömmlich gemauert wird. Er wird sich auf standardisierte Fenstergrößen beziehen ... Nur unter diesen Voraussetzungen können Bauzeit und Baukosten in einem realistischen Rahmen gehalten werden.

Natürlich gibt es Prunkbauten, aber diese sind in der Erstellung teurer, die Bauzeiten länger und Instandhaltung und Wartung problematischer.

Dasselbe gilt für Software Architekturen. Die Architektur legt fest, was wie gebaut werden soll. Werden hier best practice Ansätze (z.B. in Form von SW Pattern), Art und Beschaffenheit von Halbzeugen (RTOS, BSP, Eigenschaften der Hardware, ...) nicht berücksichtigt, dann schlägt sich das direkt auf die Produktivität in der folgenden Implementationsphase nieder.

Architektur bedeutet im wesentlichen, Rahmenbedingungen für die Implementierung vorzugeben. Ziel ist es mit diesen Rahmenbedingungen dafür zu sorgen, dass sowohl die Implementierung selbst effizient durchgeführt werden kann, als auch dass ein homogenes, strukturiertes, effizientes System entsteht. Und die Rahmenbedingungen sind heute nicht trivial. Evtl. arbeiten verschiedene Entwickler an verschiedenen Standorten mit unterschiedlichen Technologien. Alte, neue und zugekaufte Software Komponenten müssen vereint werden, Varianten und Versionen entstehen oder existieren ... all das soll am Ende zu einem robusten, wartbaren, änderbaren System verschmelzen. Die Architektur legt den Grundstein dafür.


„Architektur ist der Dreh- und Angelpunkt für die hochkomplexen Systeme, die wir jetzt und in Zukunft brauchen“

Quelle: Rolf Siegers, Raytheon

Die Architektur ist immer eine Gradwanderung zwischen der Beibehaltung von etablierten oder Einsatz von neuen Mustern, Wiederverwendung oder Neuentwicklung, Eigenentwicklung oder Zukauf von Komponenten. Das macht es so schwierig, denn wer weiß im Vorhinein, was günstiger sein wird, eine alte Komponente anzupassen oder diese besser neu zu entwickeln. In der Regel wird man es nicht einmal am Ende des Projektes wissen, denn es wurde sich ja für einen Weg entschieden und nur dieser durchgeführt. Der Ausgang des anderen Weges bleibt für immer Spekulation.

∨ mehr Text anzeigen

Aus meinen Erfahrungen hat die 2. Software Engineering Sicht der Architektur wesentlich größeren Einfluss auf das obige Ziel, als die Applikations Sicht. Auch hier noch einmal eine analoge Betrachtung.

Ein Architekt wird zur Konstruktion eines Bürogebäudes, mit der Anforderung die Raumaufteilungen im Lebenszyklus variieren zu können, in Form einer Skelettbauweise (keine tragenden Wände) planen. Hingegen wird er ein Einfamilienhaus ohne diese Anforderung herkömmlich mit tragenden Wänden konstruieren, da dieses preiswerter ist.

Würde die Anforderung der variablen Raumaufteilung nicht im Vorhinein berücksichtigt und das Bürogebäude mit tragenden Wänden konstruiert (Preiswerte Entstehungskosten), ist im Nachhinein die Veränderung der Raumaufteilung mit großem Aufwand verbunden. Die nachträgliche Umgestaltung der Konstruktion in eine Skelettbauweise wäre aus ökonomischer Sicht nicht realistisch.

Genau so verhält es sich mit der Architektur eines Software Systems. Wird z.B. ein Software System mit einer kooperativen Laufzeitarchitektur in Verbindung mit synchroner Datenkommunikation implementiert und im Nachhinein entsteht die Anforderung einer preemtiven Lauzeitarchitektur, dann kann dies nur mit erheblichem Aufwand eingeführt werden. In der Praxis würde man nicht die grundlegende Architektur ändern, sondern nur einzelne Tasks preemptiv gestalten, jedoch mit großen Nebenwirkungen auf der Kommunikationsebene, da diese synchron ist.

Dieser nun entstandene architektonische Kompromiss ist die Basis für so genannte Software Erosion. Die Architektur ist nun nicht mehr homogen, und damit empfindlich für unvorhersehbare Effekte bei weiteren Änderungen. Damit einhergehend sinken Robustheit, Änderbarkeit ...

Besser wäre gewesen die Anforderung für eine kooperative oder preemptive Laufzeitarchitektur mit einem weitsichtigen Design zu planen.

Ein Laufzeit-Pattern gehört aus meiner Sicht nicht zum eigentlichen Applikations Design. Ich verstehe ihn als ein Halbzeug. Es entspricht einem Nagel oder einer Schraube. Möchte ich eine Verbindung haben, die einfach wieder gelöst werden kann werde ich eine Schraube benutzen. Aus rein funktionaler Sicht auf das System macht es keinen Unterschied.

Erst aus Lifecycle Sicht unter Berücksichtigung der Wartung entsteht ein Unterschied. Wenn ich mich an der einen Stelle für eine Schraube entscheide liegt der Gedanke nahe für alle Verbindungen eine Schraube zu nehmen. Ebenso wird man sicher einheitlich Torx oder Kreuzschlitz Schrauben anwenden und nicht beides in Kombination. Der Vorteil besteht darin, dass in der Implementierung nur eine Art Werkzeug benötigt wird. Stellen Sie sich vor, Sie müssten einen Schrank zusammen bauen in dem gemischt Kreuzschlitz, Torx, Imbus und Schlüsselschrauben benutzt würden. Bei jeder zweiten Schraube müssten Sie das Bit des Akkuschraubers wechseln oder gar diesen gegen einen Schraubenschlüssel austauschen. Ihre Produktivität würde sicher nicht optimal sein.

Das gleiche gilt im SW Engineering. Gleichförmige Verwendung der Konstruktionsmittel sorgt für effiziente Implementation, für bessere Verstehbarkeit, Wartbarkeit ...

Auswahl und Beschreibung der Konstruktionsmittel möchte ich als Architektur Design bezeichnen, das Design aus Sicht der funktionalen Anforderungen der Applikation als Software Design. Die Trennung des Designs in diese beiden Kategorien ist nicht Stand der Technik und die von mir zugewiesenen Bedeutungen der Begriffe unüblich, aber ich halte es für sehr hilfreich diese beiden Fassetten im Design zu trennen.

Architektur Design und Architektur Muster

Der Architekt verbindet Design mit Baukonstruktion. Die Baukonstruktion fußt auf DIN Maßen für Halbzeuge (Ziegelmasse, Fenstergrößen ...) und Best Practice Mustern (Konstruktionsmuster für Wetterschutz, kurze Wege, Barrierefreiheit ...)

Auch im Software Engineering wird auf Kategorien und zugehörige Muster und Normen zurückgegriffen. Je nach Kategorie und Anforderung werden dann betriebsbewährte Pattern eingesetzt. Information Hiding, Publisher Subscriber, Instanziierung, Ereignisse oder Signale, Scheduling Pattern ...

Die Kunst des Architekten liegt darin die grundlegenden Eigenschaften einer Applikation zu erkennen und dementsprechend geeignete Muster auszuwählen und deren Anwendung als Rahmensatz zu definieren. Auf dieser Basis wird dann die Systemarchitektur aufgebaut.

Was zeichnet ein gutes Architektur Design aus?

Um diese Frage zu beantworten, müssen wir erst einmal definieren was denn eigentlich das Ziel der Software Architektur ist. Nun, das ist relativ einfach, die Basis Idee einer jeden Software Architektur ist die Anwendung eines seit tausenden von Jahren erfolgreich angewandten Mechanismus: ,divide et impera‘ (teile und herrsche), im Engineering auch als reduktionistischer Lösungsansatz bezeichnet.

Wenn ein Trend im Engineering sicher ist, dann ist es das Wachstum der Komplexität. Nahezu genau so sicher ist die zunehmende Geschwindigkeit mit der die Komplexität wächst. Es geht darum diesem Trend entgegenzuwirken in dem ein komplexes System so lange in kleinere Teile zerlegt wird, bis deren Komplexität beherrschbar ist. Anschließend wird aus diesen Teillösungen eine Lösung für das Gesamtproblem (re-)konstruiert.

Aber nicht immer scheint dieser Ansatz zum Ziel zu führen, was dann fast immer an Art und Ausprägung der Schnittstellen liegt. Betrachten wir uns den heutigen Charakter vieler Architekturen einmal auf Basis der Schnittstellen.

Dort entsteht heute trotz der Anwendung von ,divide et impera‘ die Komplexität. (Siehe Abbildung Nr. 1) In einem System wächst die Anzahl der Beziehungen exponentiell zur Anzahl der Elemente.

Nun werden sicher einige Softwareentwickler bedenklich und behaupten: Aber nicht jedes Software Modul (Element) hat eine Beziehung zu jedem anderen Modul. Und ich gebe ihnen recht. Denn jedes Modul hat mehrere Beziehungen zu jedem anderen Modul. So haben Sie es nicht gemeint? Ich weiß, und werde erklären, dass es doch so ist. Betrachten wir Funktionen in einer einfachen Laufzeitarchitektur, der so genannten main() Loop. Dort werden alle Funktionen nacheinander abgearbeitet. Die Periodizität aus zeitlicher Sicht ergibt sich für jede einzelne Zeile Code durch die Laufzeit der gesamten main() Schleife. Wird nun in nur einer Funktion durch eine Änderung die Laufzeit erhöht, dann ändert sich die Periodizität für alle anderen Funktionen. Das kann unmerklich und ohne funktionale Änderungen geschehen, aber evtl. nach der 384. Änderung wird die minimale Reaktionszeit einer der Funktionen überschritten und das hat nun doch funktionales Fehlverhalten zur Folge.

Neben der Zeitebene haben wir im Software Engineering weitere Ebenen.

Aus Sicht des Software Engineering sind das:

  • Zeit
  • Datenfluss
  • Logik (Kontrollfluss)
  • Prioritäten (bei preemptiven Systemen)

Aus Sicht der Applikation kommen weitere Ebenen hinzu:

  • Versionen (Varianzen über die Zeit)
  • Varianten (Varianz zum Zeitpunkt der Produktion)
  • Betriebsmodi (Varianz zum Zeitpunkt des Betriebes)
  • Überlagert Kontrollflüsse (Notaus, Errorhandling ...)
  • ...

In der Praxis sind in einem Design 5 bis 10 Ebenen zu berücksichtigen. Auf all diesen Ebenen können sich die Einheiten (wie immer wir sie bezeichnen, Module, Funktionen, Prozesse, Klassen, Objekte ...) gegenseitig beeinflussen. Sicher bezogen auf eine Ebene hat ein Element nicht unbedingt eine Beziehung zu jedem anderen, aber über alle Ebenen hinweg betrachtet sind es häufig sogar mehrere.

Nun ist jedoch ,teile und herrsche‘ eine der effizientesten Möglichkeiten Komplexität beherrschbar zu machen. In den meisten Architekturen wird das ,teile‘ jedoch nur sehr halbherzig implementiert. Oftmals nur auf der statischen Ebene. Eine der Hauptaufgaben des Architektur Designs ist es, die Entkopplung auf so viel Ebenen wie möglich zu erreichen. Damit haben wir eine Qualität in Bezug auf obige Frage, was ein gutes Architektur Design ausmacht, identifiziert. Es ist die Qualität der Schnittstellen.

Wie wir schon gesehen haben ist die main() Loop kein geeigneter Laufzeitpattern, um eine Entkopplung auf der Zeitebene zu bekommen. Kooperatives Multitasking ist besser, preemptives Multitasking noch besser. Je nach Anforderung werden Pattern ausgewählt, die das notwendige Maß an Entkopplung ermöglichen.

Wir werden jedoch keine einhundert prozentige Entkopplung auf allen Ebenen erreichen, was auch gar nicht sinnvoll wäre, denn auf einer Ebene müssen Kontrollfluss und/oder Datenfluss stattfinden. Aber wir können eine Ebene definieren auf der die notwendige Kommunikation stattfindet und dann mit geeigneten Pattern Einflüsse aus anderen Ebenen so weit wie möglich verhindern. (Encapsulation)

Die Frage ist nur auf welche Ebene konzentrieren wir uns und auf welchen anderen Ebenen sorgen wir für größtmögliche Entkopplung? Die Antwort ist abhängig vom Charakter der Applikation.

Grundsätzlich kann eine Software Applikation in zwei Charaktere unterschieden werden, daten- oder ablauforientiert. In technischen Embedded Systemen haben wir es fast immer mit einem eher ablauforientierten Charakter zu tun. Aus diesem Grund beschränken wir uns in den folgenden Betrachtungen auf laufzeitorientierte Systeme. Die Architektur der damit verbundenen Basis Ebene wird als Laufzeit Architektur bezeichnet.

Definitionen

Latenzzeit (Latency Time) ist die Zeit vom Auftreten eines Ereignisses bis zum Start der Behandlungsroutine (Operation). Diese Zeit kann auf den Einzelfall bezogen werden, sie kann auch als allgemeine Angabe (Minimum, Maximum, Durchschnittswert mit Streuung) gewählt werden.

Laufzeit (Service Time) ist die Zeit zur reinen Berechung einer Reaktion auf ein externes Ereignis. In einem deterministischen System kann diese Zeit bei gegebener Rechengeschwindigkeit prinzpiell vorherbestimmt werden.

Reaktionszeit (Reaction Time) ist diejenige Zeit, die vom Anlegen eines Satzes von Eingangsgrößen an ein System bis zum Erscheinen eines entsprechenden Satzes an Ausgangsgrößen benötigt wird.

Frist (Dead Line) kennzeichnet den Zeitpunkt, zu dem die entsprechende Reaktion am Prozess (Operation) spätestens zur Wirkung kommen muss. Diese Fristen stellen eine der wesentlichen Randbedingungen des Umgebungsprozesses dar.

Zeitanaloge Systeme sind komplett zeitkontinuierlich. d.h., jeder Zwischenwert zwischen zwei Zeitpunkten kann angenommen werden und ist Werte-relevant.

Zeitdiskrete Systeme werden beschrieben durch eine Funktion von abzählbar vielen Zeitpunkten. Daten gelten immer nur in Bezug zu einem dieser Zeitpunkte. Zwischenwerte können nicht angenommen werden.

Signale (Signal) repräsentieren zeitkontinuierliche Daten.

Ereignisse (Event) repräsentieren zeitdiskrete Daten.

Laufzeit Architektur Design als Fundament

Auch im Laufzeit Architektur Design haben wir es mit zwei grundsätzlichen Paradigmen zu tun.

  1. die Umwelt mit einem klassischen Ereignisorientierten Paradigma (Natürliches Paradigma)
  2. die CPU mit einem klassischen zeitgetriebenen Paradigma (Technisches Paradigma)

Der Kontrollfluss in der Umwelt liegt also im Bereich von Ereignissen, die chaotisch also in der Regel aus zeitlicher Sicht unvorhersehbar auftreten. Unsere CPU hingegen kann streng genommen nur Befehle nacheinander abarbeiten. Die Abarbeitung mehrerer Befehle ist immer mit einem zeitlichen Zyklus verbunden. In der Natur können Dinge gleichzeitig parallel geschehen, unsere CPU kann nur sequenziell arbeiten. (Abgesehen von modernen Dual Core Systemen)

In ihren Grundsätzen widersprechen sich diese beiden Ansätze und es gelten jeweils sehr unterschiedliche Gesetzmäßigkeiten. Unter der Voraussetzung einer Mindestreaktionszeit (Nyquist-Shannon-Abtasttheorem) können jedoch beide Paradigmen ineinander überführt werden. Unter der Berücksichtigung einer minimalen Reaktionszeit lässt sich also jeder Kontrollfluss entweder zeitgesteuert oder ereignisgesteuert darstellen.

Bezüglich des Laufzeit Architektur Designs unserer Software sind beide Paradigmen möglich. Auch die Kombination beider Paradigmen kann sinnvoll sein.

∨ mehr Text anzeigen

Dazu muss mindestens einmalig eine Transformation des Paradigmas stattfinden. Unsere CPU kennt nur den zeitgetriebenen Ansatz. Auf Basis der Clock Frequenz (Taktfrequenz der CPU) werden alle Befehle grundsätzlich zyklisch abgearbeitet. Aus der Clock Frequenz ergibt sich auch gleichzeitig die theoretische minimale Reaktionszeit. Theoretisch deshalb weil die CPU in der Regel mehrere Operationen abarbeiten muss und eine Operation aus mehreren Befehlszyklen besteht.

Wir könnten also diesem Paradigma folgen und die Abarbeitung zyklisch zeitgesteuert realisieren. Hier läge der Paradigmenwechsel an der Grenze des Software Systems zur Außenwelt.

Als Alternative könnte die CPU auf Basis ihrer Interrupt Eingänge die Umwelt (Sensorik) abtasten und wenn eine signifikante Änderung erkannt wird ein Ereignis an die Systemebene schicken. Hier würde der Paradigmenwechsel bereits auf der Interrupt Ebene der CPU stattfinden, unsere Systemsoftware würde auf Basis des ereignisorientierten Paradigmas realisiert.

Grundsätzlich sind beide Ansätze verbreitet und haben ihre spezifischen Einsatzgebiete. Welcher Ansatz in welchen Fällen besser geeignet ist, dazu später mehr. Zuerst wollen wir nun einen Blick auf die Datenebene werfen.

Kommunikations Design

Auch in der Art der Kommunikation können wir zwischen zwei grundsätzlichen Paradigmen unterscheiden.

  1. aus zeitlicher Sicht kontinuierlicher Datenstrom (als Signale bezeichnet)
  2. aus zeitlicher Sicht diskreter Datenstrom (als Ereignisse bezeichnet)

Der ureigene Charakter beider Datenströme kann mit einem Schalter und Taster verglichen werden. Ein Schalter repräsentiert zu jedem Zeitpunkt seine eingestellten Daten (Zustand) z.B. Ein und Aus. Er kann also zu jedem Zeitpunkt abgefragt werden und wird das korrekte Datum liefern. Wollen wir mit einem Taster dauerhafte Zustände repräsentieren würden wir seine Wirkung (Daten) eher als Impuls (Ereignis) sehen. Das betätigen des Tasters würde ein Ereignis darstellen, das den Wechsel vom Zustand Ein zum Zustand Aus repräsentiert. Der Taster erzeugt ein zeitdiskretes Signal. Der Taster kann nicht zu jedem Zeitpunkt abgefragt werden. Er würde außer im Zeitpunkt der Betätigung immer Aus als Datum liefern, obwohl sich das System evtl. im Zustand Ein befindet.

Vielleicht erkennen Sie schon die Analogie zum Laufzeit Architektur Design.

Ein zeitgetriebenes Design harmonisiert besser mit kontinuierlichen Daten. Da die Abarbeitung einer Operation über die Zeit gesteuert wird, ist es ideal, wenn diese Operation genau zu diesem Zeitpunkt auf gültige Daten zugreifen kann. Bei diskreten Daten müsste erst überprüft werden ob der Zeitstempel des Datums die aus zeitlicher Sicht korrekten Daten beinhaltet.

Im Gegenzug harmonisiert ein ereignisgetriebenes Design besser mit diskreten Daten. Diese können direkt zur Ansteuerung des Ablaufs genutzt werden. Beim Druck des Tasters wird die entsprechende Operation ausgeführt, die die Funktion des Tastendrucks repräsentiert.

Grundsätzlich gilt also: zeitgetriebene Laufzeitarchitekturen harmonisieren besser mit kontinuierlichen Daten (Signalen) und ereignisgetriebene Laufzeitarchitekturen mit diskreten Daten (Ereignisse, Nachrichten).

Synchrone und asynchrone Kommunikation

In der Praxis tauchen häufig auch die Begriffe synchrone und asynchrone Kommunikation auf. Grundsätzlich können diese Begriffe analog der obigen Daten (Signale und Ereignisse) gesehen werden. Findet der Zugriff auf ein Datum im selben Augenblick der Verarbeitung statt (Signal), sprechen wir von synchroner Kommunikation. Kann ein Datum unabhängig vom Zeitpunkt der so genannten Diskretisierung stattfinden, sprechen wir von asynchroner Kommunikation, repräsentiert von einem Ereignis in Kombination einer Queue (Warteschlange).

(Ein Ereignis wird in eine Queue gelegt und erst zu einem späteren Zeitpunkt, sozusagen asynchron, ausgelesen und verarbeitet)

Vor allem in Zusammenhang mit preemptiven Systemen wird dieser Aspekt wichtig. Können sich Operationen gegenseitig unterbrechen, die auf die selben Daten zugreifen, kann das zu Korruption führen. Ein veralteter Mechanismus ist, die Daten dann mit einem so genannten Semaphor (Ampel zur Kennzeichnung, wer gerade Lese- bzw. Schreibrechte hat) zu schützen. Das kann jedoch zu ungewünschten Verdrängungen bis hin zu Verriegelungen auf der Zeitebene führen, was nicht im Sinn der OO Encapsulation ist.

Die Alternative ist mit asynchronen Daten zu arbeiten. Die schreibende Operation sendet die Daten in Form von Ereignissen (in diesem Fall mit dem Datum als Inhalt in Form einer Message) an die lesende Operation. Preemptive Laufzeitsysteme sollten also grundsätzlich mit asynchronen Daten kombiniert werden.

Kontinuierliche und diskrete Signale

Die Begriffe kontinuierlich (continuous) und diskret (diskontinuierlich; discrete) beziehen sich sowohl auf die unabhängigen Variablen (Raumkoordinaten x, y, z; Raumwinkel α, β, γ; Länge l ; Zeit t; Frequenz f; Wellenlänge λ usw.) (discrete time signal, DTS) als auch auf die abhängige Variable x (discrete amplitude signal).

Ein kontinuierliches Signal liegt dann vor, wenn dieses für jeden beliebigen Wert der unabhängigen Variablen jeden beliebigen Wert der abhängigen Variablen annehmen kann. Dagegen wird ein diskretes Signal nur diskrete, das heißt, höchstens abzählbar viele Werte bezüglich der unabhängigen und / oder abhängigen Variablen annehmen. Da fast alle physikalischen Größen kontinuierlich sind, entstehen nichtkontinuierliche Signale meistens künstlich. Die zugehörigen Vorgänge zur Erzeugung diskreter Signale aus kontinuierlichen Signalen heißen diskretisieren.

Generell ist eine physikalische Größe eine Funktion von Raum und Zeit. Wegen des Aufwandes ist es nicht möglich (und oft auch nicht sinnvoll), den Wert einer Größe an jedem Raumpunkt und zu jedem Zeitpunkt zu ermitteln. Man wird also ein Netz (array) von Sensoren im Raum installieren (sensor fusion) und deren Messergebnisse nur zu bestimmten Zeitpunkten abfragen und verarbeiten (data fusion). Im Minimalfall erfolgt eine Messung nur an einem einzigen Ort und zu einem einzigen Zeitpunkt.

Quelle: Kontinuierliche, diskrete Signale Karl H. Ruhm, IWF Zürich

Die Analyse der Laufzeitanforderungen als Entscheidungsgrundlage

1. Definition der wichtigsten so genannten Operationen
Eine Operation stellt die kleinste zusammenhängende Einheit einer System Funktion dar. In der Regel wird diese Operation innerhalb einer Logik eingebunden, um die spezifische Funktion zum gewünschten Zeitpunkt zu bewerkstelligen. (Zur Ermittlung der Operationen siehe auch Harmony/SE).

Es wird bewusst der Begriff ,Operation‘ verwendet, um Begriffe wie Methoden oder Funktionen zu vermeiden und damit unabhängig von einer bestimmten Notation zu bleiben.
 

2. Abschätzung der Zeiten
Nun müssen für die definierten Operationen folgende Zeiten ermittelt werden.

  • min. Reaktionszeit bzw.
  • min. Zykluszeit
  • max. Laufzeit

Je nach natürlichem Charakter (Zyklisch oder ereignisorientiert) wird entweder die Zyklus- oder die Reaktionszeit angegeben. Um die grundlegenden Laufzeitarchitektur Anforderungen zu finden reicht oft eine grobe Abschätzung im Bereich von Zehner Potenzen.

Um die grundlegenden Laufzeitarchitektur Anforderungen zu finden reicht oft eine grobe Abschätzung im Bereich von Zehner Potenzen.

Best Practice Tip

Achtung: sehr häufig wird das natürliche Laufzeitmuster einer Operation so definiert, wie es in der letzten Realisierung implementiert war.

War dort die Laufzeitarchitektur zeitgetrieben wird auch die Grundstruktur aller Operationen als periodisch angegeben.

Hier gilt es herauszufinden, wie das natürliche Verhalten dieser Operation ist, und nicht wie es bisher implementiert wurde.

In meinen Architektur Workshops treffe ich immer wieder auf Entwickler, die hier in ihren Denkmustern stark aus vorherigen Implementationen geprägt sind und mehrfache hartnäckige Hinweise benötigen, um den natürlichen Charakter wieder zu erkennen. (Das endet in seltenen Fällen in hitzigen Diskussionen.)

3. Analyse der Verhältnisse Reaktions- zu Laufzeit
Die Analyse welche Operationen sich hinsichtlich ihrer Reaktionszeit mit anderen Operationen in Bezug auf die Laufzeit überschneiden (Längere Laufzeiten als Reaktions- bzw. Zykluszeiten) geben uns Aufschluss auf preemptives Verhalten (Unterbrechbarkeit). Operationen mit kurzen Laufzeiten müssen Operationen, deren Laufzeit länger ist unterbrechen, um ihre Reaktionszeit zu erfüllen.
In Hinsicht eines möglichst einfachen Scheduling Pattern auf der Systemebene wird als erstes abgeschätzt, ob die notwendigen Unterbrechungen auf der Interruptebene gelöst werden können, in dem alle Operationen, die andere unterbrechen, der Interruptebene zugeordnet werden. Das sollten jedoch nicht mehr als 5 sein.
Sind mehrere Operationen davon betroffen entscheiden wir uns für preemptives Multitasking auf der Systemebene.
Alle Überschneidungen auf der Systemebene werden farblich gekennzeichnet.

    4. Ermitteln der Maximalen Latenzzeit für den Scheduling Pattern (Nur wenn Preemption auf der Systemebene notwendig ist)
    Für einen Scheduling Pattern muss eine Latenzzeit für den Taskwechsel einkalkuliert werden. Die maximale Latenzzeit ergibt sich aus dem Aufschlagen der Latenzzeit auf die Reaktionszeit der Operationen auf der Systemebene. Die maximale Latenzzeit ergibt sich aus den folgenden Punkten.

    • die Latenzzeit sollte kleiner als die minimalen Reaktions- bzw. Zykluszeiten der Operationen auf der Systemebene sein.
    • es sollten möglichst keine weiteren Überschneidungen zwischen Reaktions- bzw. Zykluszeiten und Laufzeiten auftreten.

    Jedes Scheduling verlangsamt das System. Bei zu häufigem Scheduling werden Systeme ineffizient. Im Idealfall wird die Latenzzeit so ermittelt, dass die zeitkritischen Operationen auf der Systemebene hinsichtlich ihrer Reaktionszeit gerade noch schnell genug sind und dann um eine 10ner Potenz erniedrigt.

    Sie ist in jedem Fall kleiner als die Grenzfrequenz zwischen Interrupt- und System-Ebene. Ist das nicht möglich wird ein leistungsfähigerer Microkontroller benötigt.

      5. Analyse der Laufzeiten
      Obiger Flowchart gibt eine Orientierung für die Entscheidung der Laufzeitarchitektur.

      Natürlich sollte, wie oben beschrieben, auch der Grundcharakter der Sensorik und Aktorik berücksichtigt werden.

      Als erstes macht es Sinn zu analysieren, ob ein preemptiver Scheduler auf der Systemebene benötigt wird.

      In diesem Fall ist immer ein asynchroner Datenfluss ratsam. Das würde gleichzeitig bedeuten, dass der Steuerungsfluss ebenfalls auf der Datenebene statfindet, was eine ereignisorientierte Architektur stark befürworten würde. Wird keine Preemption auf der Systemebene benötigt basiert die Entscheidung einer zeit- oder ereignisgetriebenen Architektur auf der Grundcharakteristik der Operationen und der Sensorik und Aktorik.

      Je nach dem würde der passende Kommunikationspattern dazu ausgewählt. Häufig lässt sich eine durchgehende Architektur nicht darstellen. In diesen Fällen sollten dann beide Ansätze kombiniert werden. Beachten Sie dabei die obigen Regeln.

      Nun sollten folgende Eckdaten feststehen:

      • Grenzfrequenz (Reaktionszeit) zwischen Interruptebene und Systemebene
      • Maximal zulässige Scheduling Zeit (bei Preemption auf der Systemebene)
      • Scheduling Pattern
      • Kommunikationspattern

      Die Qual der Wahl

      Abschließend möchte ich noch einmal die verschiedenen Architektur Pattern gegenüberstellen.

      Zeitgetriebene Laufzeitarchitektur (Non Preemptive)

      • Ist in der Regel einfach zu implementieren, z.B. durch einen (für verschiedene Zeitscheiben mehrere) Timer. Oder im simpelsten Fall durch die altbekannte main() Loop.
      • Harmonisiert gut mit zeitkontinuierlichen Daten (Signale) • Harmonisiert nicht gut mit zeitdiskreten Daten (Ereignisse). Dadurch nicht gut geeignet für Preemption.
      • Eignet sich sehr gut, bei so genannter passiver Sensorik und Aktorik, wenn diese abgetastet werden muss. In diesem Fall kann die Verarbeitung im selben Zeitraster geschehen.
      • Eignet sich nicht gut bei so genannter aktiver Sennsorik und Aktorik, wenn diese die Daten z.B. über einen Bus in Form von zeitdiskreten Daten kommuniziert.
      • Verhält sich in der Regel robuster gegen störanfällige Signale, da sich das System nach einem Zyklus selbständig aktualisiert wenn die Sensorsignale wieder stabil sind.

      Ereignisgetriebene Laufzeitarchitektur (Non Preemptive)

      • Laufzeitpattern sind aufwändiger zu implementieren, da eine Queue und Speicherhandling für die Bereitstellung der Ereignisse und Nachrichten benötigt wird.
      • Da die ereignisgetriebene Laufzeitarchitektur dem natürlichen Paradigma entspricht lassen sich komplexe Systeme einfacher realisieren, da die natürlichen Abläufe gedanklich nicht transformiert werden müssen. (Anmerkung: Viele Entwickler, die seit Jahren mit einer zeitgetriebenen Architektur arbeiten tun sich oft schwer mit obiger Aussage. Das ist einfach zu erklären. Diese Entwickler haben die Transformation des Grundproblems in die zeitgetriebene Ablaufsteuerung vor Jahren gemacht. Seither wird der Ablauf der Applikation in diesem Paradigma betrachtet und man hat sich daran gewöhnt und kennt sich darin gut aus. In diesem Fall würde die Betrachtung auf dem natürlichen ereignisgetriebenen Paradigma ungewöhnlich sein. Das ändert aber nichts an der Tatsache, dass das natürliche Paradigma der meisten Applikationen ereignisgetrieben ist und abgesehen vom Gewöhnungsfaktor, aus gedanklicher Sicht, in diesem Paradigma einfacher implementiert werden kann. Gerade vor kurzem hatte ich wieder eine hartnäckige Diskussion bei einem ABS System Hersteller. Nach langen Diskussionen mit mir und seinen Kollegen hat er dann eingesehen, dass keine einzige externe Situation, welche ein ABS System berücksichtigen muss, synchron zu jedwedem Takt der Software Architektur geschieht. Was wiederum nicht bedeutet, dass ein zeitgetriebener Architektur Ansatz zur Implementierung eines ABS Systems eine schlechte Wahl ist. )
      • Harmonisiert sehr gut mit zeitdiskreten Daten (Signalen) über die direkt der Kontrollfluss gesteuert werden kann.
      • Harmonisiert gut mit zeitkontinuierlichen Daten, wenn diese nicht den Kontrollfluss beeinflussen. (Z.B: Daten zur Parametrisierung)
      • Eignet sich sehr gut bei aktiver Sensorik und Aktorik, wenn diese die Daten z.B. über einen Bus in Form von zeitdiskreten Daten kommuniziert.
      • Eignet sich gut mit passiver Aktorik und Sensorik, wenn diese auf Basis von Interrupt Routinen abgetastet werden.
      • Verhält sich störanfällig gegenüber unsauberen Signalen, die falsche Ereignisse erzeugen (Überlauf der Queue bzw. Prozessorüberlastung) oder wenn Ereignisse verloren gehen.

      Preemptive Laufzeitarchitekturen

      • Auf Grund der durch die Unterbrechbarkeit entstehenden Datenkorruptionen bei synchroner Kommunikation empfiehlt sich grundsätzlich asynchrone Kommunikation einzusetzen.
      • Diese harmonisiert wiederum nicht gut mit einer zeitgetriebenen Laufzeitarchitektur. Das impliziert, dass sich preemptive Architekturen grundsätzlich einfacher und robuster in Form von ereignisgetriebenen Laufzeitarchitekturen implementieren lassen.

      Damit gelten ansonsten die gleichen Gesetze, wie für die ereignisgetriebene Architektur.

      Um den Nachteil der Störanfälligkeit gegenüber unsauberen Sensor-Signalen zu berücksichtigen kann eine Mischung der Architekturansätze sinnvoll sein. Auf der untersten Ebene arbeitet eine laufzeitgetriebene, auf der oberen Ebene eine ereignisgetriebene Architektur.

      Mischung von Laufzeitparadigmen, Einbindung von Interrupt Service Routinen

      In der Praxis kommt eine Mischung der Paradigmen sehr häufig vor. Oft sind z.B. Regeln mit einer festen Zeitperiode auf der Interrupt Ebene implementiert. Die Logik, in der sie parametriert werden, findet aber auf der Systemebene statt auf Basis einer ereignisorientierten Architektur. Nun ist die spannende Frage: wie kommunizieren die beiden Ebenen miteinander?

      Das gilt nicht nur für die Mischung von Laufzeitparadigmen, sondern auch für die Kommunikation zwischen zwei Ebenen von der die eine gegenüber der anderen unterbrechbar (Preemptiv) ist.

      Hier haben wir immer das Problem der Datensynchronisation. Liest eine Operation gerade einen Datensatz und wird innerhalb dieses Lesens von einer Operation unterbrochen, die genau diese Daten schreibt, kann das zu korrupten Leseergebnissen führen.

      Wie bereits beschrieben ist der übliche Weg nun mit so genannten Semaphoren zu arbeiten. Diese verlagern jedoch die Zeitverschiebung in der Regel auf die zeitkritische Ebene. (z.B. in Form von globalen Interrupt-Sperren.)

      Nach dem heutigen Ansatz der Information Hiding ist das ungünstig. Besser wäre in diesem Fall eine asynchrone Kommunikation. Diese basiert bei vielen Echtzeitbetriebssystemen jedoch auf Basis von dynamischer Speicherallokation (maloc()), deren Nutzung in Interrupt Service Routinen äußerst kritisch ist.

      ∨ mehr Text anzeigen

       

      Hier gilt im Architektur Design der Situation genügende, effiziente Kommunikations-Pattern einzusetzen. Z.B. in Form von get() und set() Operationen, die beim Lesen ein Bit setzen und beim Schreiben das Bit rücksetzen. Nach dem Lesevorgang wird das Bit überprüft und bei zwischenzeitlichem Schreibvorgang erneut gelesen.

      Hier ist gewährleistet, dass die schnellere Ebene Vorfahrt bekommt und nicht damit rechnen muss von Ereignissen der darüber liegenden Ebene ausgebremst zu werden. Und die darüber liegende Ebene muss keine Kenntnisse über die Synchronisierung haben.

      Das war nun ein kleiner Einblick in den Bereich des Architektur Designs, fokussiert auf den Bereich Laufzeit und Daten, also wirklich nur ein kleiner Bereich. Ich hoffe die Lektüre war trotz der dieses mal 11 anstelle von 7 Seiten kurzweilig und hat bei dem einen oder anderen Leser das Interesse für diesen Bereich des SW Engineering geweckt.

       

      Literaturverzeichnis

      Handbuch Embedded Systems Engineering - Prof. Dr. Christian Siemers TU Clausthal FH Nordhausen Private FH Göttingen

      Softwarearchitektur und Anwendungsentwicklung (SAA) - Mitschrift der Vorlesung im SS 2006 von Prof. Bodo Igler, verfasst von Timo Körber www.minibar182.de

      IBM Rational Harmony/SE Deskbook Rel. 3.1.2 - Peter Hoffmann

      Kontinuierliche, Diskrete Signale - Ruhm, K. H.; Internet-Portal "Wissenschaft und Technik des Messens"; PDF

      Richtiges richtig tun, Excellence im Software Engineering - Ausgaben 24 + 25 - Andreas Willert, ESE-Reports als PDFs

      Autor:

      Andreas Willert
      Haben Sie noch Fragen oder Anregungen zum Thema? Dann freue ich mich über eine E-Mail: awillert@willert.de

      Herausgeber:

      WILLERT SOFTWARE TOOLS GMBH
      Hannoversche Straße 21
      31675 Bückeburg
      E-Mail: info@willert.de
      Tel.: +49 5722 9678 - 60

      Alle ESE-Reports im Überblick

      Aktuelles Know-how und neueste Entwicklungen und Trends rund um das Thema Emedded Software Engineering

      > zu den ESE-Reports

      Hinweis: Unsere Webseite nutzt Cookies. Wenn Sie fortfahren, nehmen wir an, dass wir Ihre Erlaubnis haben Cookies zu setzen. Informationen zum Einsatz von Cookies sowie unsere Datenschutzbestimmungen finden Sie in unserer Datenschutzerklärung und über unser Impressum.