Die Hälfte aller Embedded-Designs sind Mehrprozessorsysteme. Dieser Anteil wächst stetig. Mit immer weiteren Multicore-Designs steigen auch die Herausforderungen an die Softwareentwickler. Weil langfristige Erfahrungen auf diesem Gebiet noch fehlen, kursieren im Hinblick auf die Multicore-Softwareentwicklung viele Spekulationen, anderes stützt sich auf Fakten. Für den Entwickler stellt sich die Frage, was ist wahr, was ist Dichtung.
Eine falsche Annahme ist die Vermutung, dass das Überarbeiten von Embedded-Software eine große Herausforderung darstelle, um Parallelabläufe zu erhalten. Die meisten Embedded-Systeme bieten bereits Multithreading, weswegen Embedded-Entwickler damit vertraut sind, Echtzeit-Betriebssysteme einzusetzen, die eine Form von Threading-Primitive besitzen. In einem System mit einem Core sind Threads quasi parallel ablauffähig. Ein Multicore-Prozessor (mit SMP-fähigem RTOS) weist echte parallele Threads auf und benötigt dafür normalerweise keine Softwareänderungen. Da Embedded-Systeme immer komplexer werden, findet bei neuen Komponenten ein automatisches Mapping auf die Threads statt. Ein Web-Server, zum Beispiel, benutzt ein oder mehrere Threads, um die Anfragen zu bedienen. Doch nicht alle Systeme nutzen sämtliche Cores optimal. Entwickler können durch Anpassen des Codes die Parallelität erhöhen. Ebenfalls ein Irrtum: Man müsse beim Überarbeiten von Software Threads maximieren und Prozesse minimieren. Es gibt viele Möglichkeiten, Gleichzeitigkeit bei der Abarbeitung zu erreichen. Grobe Parallelisierung wird dabei wohl bevorzugt, ist übertragbar und effizient. Bei der Entscheidung, eine neue Komponente in einen Thread (Speicherplatz mit anderen Threads teilen) oder Prozesse zu mappen, neigen die meisten Entwickler zur schlechteren Thread-Wahl. Ein Grund könnte sein, dass gängige Echtzeit-Betriebssysteme in den 80-er und 90-er Jahren keine Speicherschutzmechanismen boten. Entwickler gewöhnten sich an Threads und in vielen Fällen hat sich das bis heute nicht geändert. Mit den modernen Echtzeit-Betriebssystemen jedoch, die speichergeschützte Prozesse unterstützen, unterscheiden sich die Kosten – dank der schnellen Core- und Speicherarchitekturen – für Prozesse im Vergleich zu Threads kaum. Stattdessen sollten Entwickler zwischen Threads und Prozessen ein Verhältnis von 1:1 anstreben. Jede speichergeschützte Komponente sollte demnach eine minimale Anzahl an Threads enthalten. Idealerweise ist für jede dieser Komponenten jeweils ein Entwickler zuständig mit klar definierten, Message-basierten Schnittstellen zwischen den einzelnen Komponenten. Mit dieser Art von Systemdesign lassen sich viele Probleme bei der Synchronisierung vermeiden. Nicht ins Reich der Hypothesen gehört die Feststellung, dass die Branche bislang an mangelnder Multicore-Standardisierung leidet. So fehlen der Multicore-Software Standards in wichtigen Bereichen wie Multithreading, prozessinterne Kommunikation und Data-Plane-Beschleuniger. Multithreading ist durch den Standard POSIX gut definiert: eine Sammlung offener APIs, die von der IEEE für Betriebssystemdienste spezifiziert wurde. Der Standard bietet Schnittstellen zur Thread- und Prozesssteuerung und findet sich in Linux, Unix und einer Reihe von Embedded-Betriebssystemen wie Integrity von Green Hills Software, Lynx OS und Qnx. Aufgrund der weiten Verbreitung dieses Standards steht umfangreicher Applikations-Code zur Wiederverwendung bereit. Ein weiterer Vorteil des Standards ist die unabhängige Konformitätsvalidierung seitens der Open Group. Doch POSIX ist nicht die einzige Standardisierungsbestrebung für Multithreading. Multithreading wird in Programmiersprachen wie Java und Ada integriert und OpenMP erlaubt Entwicklern, parallele Anweisungen in C- und C++-Code zu integrieren. Jedoch reicht keiner der anderen Standards an die breite Anwendbarkeit von POSIX heran. Message Passing nutzte man im wissenschaftlichen Bereich schon immer, um Parallelverarbeitung umzusetzen. Obwohl sich die Zielapplikationen von wissenschaftlichen Anwendungen unterscheiden, erfordern Multicore-Embedded-Systeme oft eine prozessinterne Kommunikation (Interprocess Communication, IPC), um ihrerseits Parallelismus zu erzielen. Einige IPC-Mechanismen enthalten Fehlertoleranz-Merkmale wie automatisches Erkennen einer Verbindungsunterbrechung und erneute Übertragung. Ein Beispiel ist die transparente prozessinterne Kommunikation (Transparent Interprocess Communication, TIPC), die eine zuverlässige Nachrichtenübertragung, Übertragungswiederholung und Ausfallsicherheit des Kommunikationskanals gewährleistet. Ein weiterer Standard ist LINX, eine Open-Source-Version des Link-Handlers, der im OSE-Betriebssystem von Eneas zur Verfügung steht. QNX kündigte Quellcode-Verfügbarkeit für sein TDP-Modell (Transparent Distributed Processing) an. Die Multicore Association entwickelt einen IPC-Standard namens MCAPI. Und hier zeigt sich das Problem: Es gibt keinen Konsens. Denn Entwickler gehen auf Nummer Sicher und verwenden ihre eigene, generische API, die sich an verschiedenen Standards orientiert. Darüber hinaus koppeln viele Multicore-Architekturen einen General-Purpose-Prozessor (GPP) mit einem oder mehreren applikationsspezifischen Coprozessoren. Durch diese applikationsspezifische Eigenart liegt überhaupt keine API vor, auf die sich Entwickler verlassen könnten. So muss der Anbieter eines Betriebssystems eine Umsetzung der API des Komponentenherstellers bereitstellen, um dem Entwickler die optimale Nutzung des Bausteins zu ermöglichen. So wie Offload-Engines eine ähnliche Funktion bieten, sollte man auch standardisierte APIs entwickeln und verwenden. Damit wäre es denkbar, dass die Anbieter von Netzwerkprozessoren einige APIs zur Paketdatenverwaltung standardisieren.Last but not least – noch ein Märchen: Multicore-Debugging-Tools sind langsam. Nein. Führende integrierte Entwicklungsumgebungen (IDE) unterstützen Multicore schon seit langem. Für den Entwickler heißt dies, er sollte wissen, welchen Umfang moderne Multicore-Debugging-Toolpakete bieten. Eng gekoppelte Multicore-Prozessoren besitzen meist eine Debug-Schnittstelle (beispielsweise JTAG), mit der man alle Cores gleichzeitig steuern kann. Bei der Board-Bring-up- und Gerätetreiberentwicklung wird diese Schnittstelle intensiv genutzt.
Debugging-Tools sind wichtig
Für ein effizientes Multicore-JTAG-Debugging heißt das nun, dass das Entwicklungstool die Auswahl aller möglichen Kombinationen der zu debuggenden Cores bereitstellen muss – jede davon in einem eigenen Fenster. Gleichzeitig muss das Tool die Steuerung für synchrones Starten und Halten der Cores ermöglichen. Ein entsprechendes Entwicklungssystem bietet Green Hills mit den Programmen Probe und Multi IDE.
Das Run-Mode-Debugging ist für Multicore-Systeme ebenfalls von Bedeutung. Dabei werden Cores nie angehalten. Der Debugger kontrolliert die Applikations-Threads über einen Kommunikationslink wie Ethernet. Um dieses Leistungsmerkmal effizient zu nutzen, muss das Betriebssystem einen integrierten Debug-Agenten enthalten, der flexible Optionen zur Kontrolle der Software ermöglicht. Das Betriebssystem Integrity bietet einen solchen Agenten, der das Debugging jeder Kombination von User-Threads auf jedem beliebigen Core erlaubt. Der Anwender kann spezielle Breakpoints setzen, über die sich anwenderdefinierte Thread-Gruppen anhalten lassen. Einige Fehlerklassen erfordern diese feine Steuerungsmöglichkeit. Viele Hersteller von Betriebssystemen bieten auch ein Event-Tracking-Tool. Das bedeutet, ein Agent im Zielsystem hält Serviceaufrufe, Interrupts, Kontextwechsel und anwenderdefinierte Ereignisse fest. Das Tool lädt die aufgezeichneten Daten hoch (live oder post mortem) und stellt die Ereignisse zeitabhängig dar. Der Anwender hat Zoommöglichkeiten, kann bestimmte Ereignisse für weitere Informationen auswählen und Ausführungsberichte generieren. Ein entsprechendes Tool dieser Art für die Entwickler von Multicore-Software ist der Event-Analyzer von Green Hills. Das Werkzeug stellt das Systemverhalten verständlich dar und macht Leistungsengpässe, Livelocks und andere Probleme sichtbar. So genanntes Multicore-Trace ist eine weitere, immer beliebter werdende Möglichkeit zur Systemanalyse. Mit einem On-Chip-Trace (inzwischen bei immer mehr Multicore-Prozessoren vorhanden) lässt sich das Verhalten mehrerer Cores hardwarenah aufzeichnen. Trace-Analyse-Tools wandeln diese Daten in Ansichten einer höheren Ebene um. Der Entwickler kann die Softwareausführung wiederholen und sehen, welcher Core welche Funktionen zu einem beliebigen Zeitpunkt ausgeführt hat. Natürlich gibt es hinsichtlich der Architektur, dem Design und den Standardisierungsstrategien für Multicore-Entwickler noch vieles zu tun. Tröstlich ist aber, dass es inzwischen verlässliche Tools gibt, mit denen man die sich weiterentwickelnde Hardwarelandschaft beherrschen und die zur Verfügung stehende Leistungsfähigkeit voll nutzen kann.
• more@click-Code: EEK80101
