Softwaredesign

In kleinen Schritten

Partitionierung von Software in Multiprozessorsystemen

26.09.2006 | Redakteur: Martina Hafner

Die Partitionierung von Software für Multiprozessorsysteme ist eine der großen Herausforderungen des Architekturdesigns. Sie wird umso schwieriger, wenn ein System harte Echtzeitfunktionen, aber auch...

Die Partitionierung von Software für Multiprozessorsysteme ist eine der großen Herausforderungen des Architekturdesigns. Sie wird umso schwieriger, wenn ein System harte Echtzeitfunktionen, aber auch zeitunkritische Dienste beinhaltet. Wie sich diese Aufgabe Schritt für Schritt bewältigen lässt, erklärt Echtzeitguru David Kalinsky. Die Zerlegung eines komplexen Embedded-Systems direkt in Tasks lässt sich aufgrund deren hoher Anzahl mit oft zahlreichen Funktionen häufig schwer in den Griff bekommen. Deshalb ist es sinnvoll, die Aufgaben der Software schrittweise zu identifizieren. Zunächst gilt es größere Bereiche der Systemfunktionalität festzulegen, die sich später in einzelne Tasks herunterbrechen lassen. Viele Betriebssysteme bieten dafür einen speziellen Mechanismus an. Softwareanteile werden in „Containern“ zusammengefasst, die eine Reihe von Software-Tasks beinhalten. Sie haben in verschiedenen Betriebssystemen unterschiedliche Bezeichnungen. In Embedded Linux werden Container als Prozesse und Tasks als Threads bezeichnet. Im Echtzeit-Betriebssystem (RTOS) OSE heißen Container Blocks und Tasks Prozesse. Andere Echtzeit-Betriebssysteme verwenden die Begriffe Address Spaces und Partitionen. Betriebssysteme, die solche Stückelungs- oder Containerisations-Mechanismen unterstützen, gestatten den Aufbau von Speicherschutzbarrieren zwischen den Containern. So lassen sich Tasks, die in einem Container enthalten sind, vor Fehlern durch inkorrekte Tasks anderer Container schützen. Jeder Container kann einen eigenen lokalen Vorrat an RAM-Speicherpuffern haben, damit Tasks anderer Container ihre Pufferkapazität nicht ausreizen können. Auf diese Weise werden Fehler lokalisiert, bevor sie andere Container beeinträchtigen. Schritt 1: Container festlegen Die Festlegung der Container ist ein erster Schritt beim Design eines komplexen Systems. Jeder Container kann eine Reihe von Tasks enthalten, die gemeinsam einen Dienst realisieren. Dieser sollte von den Diensten anderer Container möglichst unabhängig sein. Die Aufteilung eines Systems steht im engen Zusammenhang mit seinen Applikationsdiensten und Aufgaben. Ein Pflichtenheft kann als Leitfaden für eine erste Zerlegung des Systems in Container dienen. In komplexen Systemen bieten Container häufig die folgenden Hauptdienste:-automatische Kontrolle,-Koordinierung anderer Dienste,-Datenerfassung,-Datenanalyse,-Server, häufig mit Datenspeicher oder I/O-Geräten,-Anwenderdienste,-Systemdienste wie Task Scheduling, Netzwerkkommunikation oder Dateimanagement.Ein Beispiel für ein System, das eine Reihe unterschiedlicher Container umfasst: In einer Applikation ermittelt und erfasst ein Container medizinische Daten in Echtzeit anhand regelmäßig eingehender Signale wie Körpertemperatur, Blutdruck und Gehirnströme. Wäre das System auch mit der computergesteuerten Verabreichung von Arzneimitteln oder Nahrung befasst, dann würde es hierfür einen separaten Container „Control Fluids Infusion“ zur automatischen Steuerung benötigen. Soll es mit Ärzten und Krankenschwestern durch Sprachein-, und -ausgabe kommunizieren, bräuchte es ferner den Container „Human Voice Communication“. Die verschiedenen Betriebssystemdienste, welche die Prozessoren im Multiprozessorsystem unterstützen, wären in Systemdienste-Containern untergebracht, einer je Prozessor. So hätte ein Prozessor mit Embedded Linux einen Container „Linux System Services“ und ein Prozessor mit Echtzeit-Betriebssystem einen Container „RTOS System Services“.Schritt 2: Zerlegung in TasksSobald eine grundlegende Partitionierung einer Applikation in Container auf höherer Ebene durchgeführt wurde, sollten diese intern weiter in einzelne Tasks zerlegt werden. Jede Task ist sequenziell und kann sowohl parallel mit anderen Tasks in ihrem Container ablaufen, als auch parallel mit Tasks in weiteren Containern. Dabei ist zu beachten, dass jede einzelne Task Dutzende oder Hunderte von Software-funktionen umfassen kann. Eine Task ist nicht die kleinste Einheit eines Softwarearchitektur-Designs. Sie besteht meist aus mehreren Funktionen, die innerhalb der Task sequenziell ausgeführt werden.Um bei dem Beispiel aus der Medizin zu bleiben: Der Container zur Erfassung der Daten der vitalen Funktionen könnte mehrere Tasks enthalten, die mit Sensoren kommunizieren, die Patientendaten erfassen und medizinische Messdaten zur Verfügung stellen. Tasks der Gerätesteuerung könnten zum Beispiel die Erfassung der Körpertemperatur, die Erfassung des Blutdrucks, usw. sein. Der Task für die Erfassung des Blutdrucks könnte seine Werte einer weiteren Task zur Datenanalyse zur Verfügung stellen. Diese würde Datenfilterungen, Wellenformanalysen und eventuell Musterkennungen an ihren Eingängen durchführen. Der Container zur Datenerfassung kann also eine Pipeline verschiedener Tasks jedes Sensors enthalten, von denen er Daten bezieht.Schritt 3: Zeitgrenzen beachten Die meisten komplexen Systeme beinhalten eine Kombination aus harten und weichen Zeitgrenzen sowie zeitunabhängiger Software. Deshalb kann es sinnvoll sein, Prozessoren zu haben die Linux ausführen, während andere mit einem Echtzeit-Betriebssystem laufen. Bei Standard-Linux sind Latenzzeiten in Größenordnungen von 10 bis 100 ms zu erwarten. Der neue Linux-Kernel 2.6 ist zwar eine enorme Verbesserung gegenüber bisherigen Versionen, verfügt aber immer noch über eine Preemption-Latenzzeit im ungünstigsten Falle von mehreren hundert µs. Auch eigens modifizierte preemptive Linux-Kernel sind nach wie vor nicht vollständig preemptive. Dagegen bietet Linux eine passende Umgebung für viele informationsintensive höhere Anwendungen und Verwaltungsdienste. Die Zuordnung von Containern für Applikationssoftware zu Linux- oder RTOS-Prozessoren hängt neben anderen Faktoren von den Zeitanforderungen ab. Aus diesem Grund muss jeder Container entsprechend seiner Deadlines und harten und weichen Echtzeitanforderungen geprüft werden. Soll er Software verarbeiten, die harte Echtzeit einhalten muss, kann er ausschließlich auf einem RTOS-basierten Prozessor laufen. Hat er nur weiche Deadlines mit mehreren Millisekunden oder länger, kann er auch unter Linux laufen. Einige Container beinhalten eventuell Applikationssoftware, die unterschiedliche Zeitanforderungen erfüllen muss, und sowohl auf eine RTOS-basierte Laufzeitumgebung weisen als auch auf eine Linux-Umgebung. Sobald ein Teil des Containers harte Deadlines einhalten muss, kann er aber nur auf einem RTOS-basierten Prozessor laufen. Eine Alternative wäre dann die Aufteilung des Containers in zwei Container. Der erste würde die Software mit harten Deadlines beinhalten, der zweite neue Container mit der restlichen Software könnte auf Linux laufen. Schritt 4: KommunikationsdesignNachdem eine Embedded-Applikation in Container partitioniert, die Container in Tasks zerlegt und alle Zeitanforderungen beachtet wurden, ist der nächste Schritt das Design der Kommunikation zwischen den verschiedenen Tasks. Dies umfasst die Kommunikation mit anderen Tasks innerhalb des selben Containers sowie mit Tasks anderer Container. Da die Container für minimale Wechselwirkung partitioniert wurden, sollte kein großer Kommunikationsfluss zwischen ihnen stattfinden. Als beste Kommunikationsmethode hat sich das Message Passing bewährt. Es ist vom Konzept her einfach gehalten sowie intuitiv. Asynchrones Message Passing für die Kommunikation innerhalb der Tasks und der Container erlaubt ein locker gekoppeltes Design, das viele Fehler verhindert. Andere Betriebssystemmechanismen für die Intertask-Kommunikation wie Semaphoren, Mutexes, Event Flags oder Signale im Unix-Stil sind fehleranfällig, werden unhandlich oder unausführbar, sobald sie in verteilten und Multicore-Embedded-Systemen arbeiten. Bei der späteren Zuteilung der Container und Tasks auf unterschiedliche Prozessoren nutzt man möglichst exakt das selbe asynchrone Message Passing für die Kommunikation zwischen den Tasks innerhalb des selben Prozessors und für die Kommunikation zwischen Tasks, die sich auf verschiedenen Prozessoren befinden.Schritt 5: Prozessoren zuordnenDie Zuordnung verschiedener Softwareteile zu den unterschiedlichen, oft ungleichen Prozessoren muss als nächstes erfolgen. Es ist zulässig,einem Prozessor mehrere Container zuzuordnen, aber ein Container sollte nie zwischen zwei Prozessoren aufgeteilt werden. In einem heterogenen Multiprozessorsystem, bei dem Linux auf einigen Prozessoren und ein Echtzeit-Betriebssystem auf anderen läuft, müssen bei der Zuordnung von Containern zu Prozessoren die Deadlines und die Echtzeitanforderungen eines jeden Prozessors beachtet werden. Ein Linux-kompatibler Container lässt sich einem RTOS-basierten Prozessor zuordnen, wenn dieser über passende Fähigkeiten und Kapazitäten verfügt. Dagegen ist es nicht möglich, einen Container, der harte Echtzeitanforderungen einhalten muss, einem Linux-basierten Prozessor zuzuordnen.

David Kalinsky

Kommentar zu diesem Artikel abgeben

Schreiben Sie uns hier Ihre Meinung ...
(nicht registrierter User)

Kommentar abschicken

 

Copyright © 2017 - Vogel Business Media