Software ubiquitärer Systeme Übung 3: Aspekte in Betriebssystemen und AspectC++-Tutorial Michael Engel und Olaf Spinczyk Arbeitsgruppe Eingebettete Systemsoftware Lehrstuhl für Informatik 12 TU Dortmund Michael.Engel@tu-dortmund.de http://ess.cs.uni-dortmund.de/~me/ http://ess.cs.tu-dortmund.de/de/teaching/ss2010/sus/ 1
Inhalt Aspekte in Betriebssystemen Paper: Interruptsynchronisation in PURE AspectC++-Tutorial Fortführung des Tutorials aus der Vorlesung SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 2
Inhalt Aspekte in Betriebssystemen Paper: Interruptsynchronisation in PURE AspectC++-Tutorial Fortführung des Tutorials aus der Vorlesung SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 3
PURE PURE: eine aspektorientierte Betriebssystemfamilie Objektorientiertes BS für tief eingebettete Systeme C++-Klassenbibliothek Maßschneiderung des BS auf die Anwendung Alternative zu speziell für einen Zweck entwickelte BS Basis: minimale Menge an Systemfunktionen Inkrementell erweitert durch minimale Systemerweiterungen Applikation = finale Systemerweiterung Keine traditionelle Grenze zwischen BS und Applikation SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 4
Struktur von PURE CORE: concurrent runtime executive for passive and active objects NEXT: minimal nucleus extensions, z.b.: Anwendungsorienterte Prozess- und Adressraum-Modelle Blockierende (Thread-) Synchronisation Problem-orientiertes (remote) message passing Erweiterungen sind nur auf Anforderung der Anwendung im System vorhanden SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 5
PURE CORE Minimale Teilmenge von Systemfunktionen zum Scheduling von Ereignissen und/oder Aktionen Ereignisse sind als (Hardware-)Interrupts implementiert Aktionen sind (federgewichtige) Threads Vier Bausteine: Flange Abstraktionen zur Bindung von Objekten an Trap-/Interrupt-Vektoren und Propagation entsprechender Ereignisse an höhere Ebenen des Systems, Sluice Abstraktionen zur Synchronisation Interrupt-getriebener Aktivitäten auf Interrupt-transparente Art (d.h., ohne Deaktivierung von Hardware-Interrupts), Signal-box Abstraktionen zur nicht-blockierenden Synchronisation aktiver Objekte (d.h., Threads), und Threads reel Abstraktionen zur Konstruktion, zum Scheduling und zur Destruktion aktiver Objekte. SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 6
PURE CORE: Struktur Zudem: Gerätetreiber als 5. Baustein in realen Systemen In PURE: Teil von NEXT oder der Applikation CORE stellt nur Integrationsfunktionen zur Verfügung Verbinden von Treibern mit Exception-Vektoren (flange) Synchronisation von Interrupt-getriebenem Code (sluice) Treiber: Verbindung zwischen nicht-synchronisierten und synchronisierten Teilen eines PURE-basierten Systems SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 7
Pro-/Epilog-Modell Interrupt-Behandlung ist eine der wichtigsten Aufgaben eines BS In PURE: Interruptbehandlung in zwei Teilen Prolog: Aufruf als Reaktion auf Interrupt Epilog: Fortführung des Prologs, wird später ausgeführt Epiloge enthalten die weniger zeitkritischen Teile der Interrupt- Behandlung Pro-/Epilog-Synchronisationsmodell Serialisierung von Epilogen Synchronisierte Warteschlange noch nicht abgearbeiteter Epiloge First-come, first-served De- und Enqueue-Operationen können überlappend ausgeführt werden - Weiche Synchronisation durch spezielle Implementierung SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 8
Pro-/Epilog-Modell: Details Kernel wird durch eine einige Lock-Variable synchronisiert Lock wird gesetzt, wenn Thread den Kernel betritt und gelöscht, wenn Thread den Kernel verläßt ISRs dürfen währenddessen keine Kernelaufrufe tätigen, um Korruption von Kernel-Datenstrukturen zu vermeiden Daher ISRs in zwei Teilen: Prolog und Epilog Prolog enthält Instruktionen, die schnell auf Geräte-Interrupt antworten Epilog enthält alle Operationen, die mit dem Kernel interagieren Epilog wird nicht direkt aufgerufen, sondern durch eine spezielle Klasse: guard Aufgabe: Lock-Variable vor Ausführung des Epilogs prüfen Im Falle eines Kernel Locks wird Ausführung des Epilogs verzögert, bis der Lock freigegeben wird Ansonsten wird der Epilog unmittelbar ausgeführt SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 9
Pro-/Epilog-Modell: Implementierung Globale Lock-Variable wird gesetzt/gelöscht durch lock.enter() bzw. lock.leave() Quellcode-Analyse: 166 unterschiedliche Aufrufe dieser Methoden verteilt über 15 Klassen Offenbar ist Interrupt-Synchronisation ein extrem querschneidender Belang Viele Klassen in unterschiedlichen Subsystemen sind betroffen Klassendiagramm zeigt Schichtstruktur von PURE Jede (meist konfigurierbare) Schicht ist als neue Ebene im Vererbungsbaum implementiert Aufrufe der Synchronisationsprimitive werden als Erweiterung in ihrer eigenen Schicht angesehen - Daher oft in ihrer eigenen Klasse implementiert (z.b. Monitor) SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 10
Zugriffe auf globale Lock-Variable SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 11
Probleme der Interruptsynchronisation Viele Subsysteme hängen von der Implementierung der Interrupt-Synchronisations-Primitive ab...oder zumindest vom Interface hier: osek, thread, case Nicht einfach in anderen Kontexten oder Konfigurationen wiederverwendbar Wenn diese unterschiedliche (oder keine) Synchronisationsschemata verwenden Aufwendige, fehleranfällige, manuelle Codeänderungen notwendig! Subsysteme können damit nicht wirklich wiederverwendbare Komponenten werden SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 12
Probleme der Interruptsynchronisation Aus Sicht des Applikationsentwicklers: Die Schicht, die den Synchronisationscode realisiert, definiert auch automatisch das API für den Anwendungsentwickler Die Applikation darf abgeleitete Klassen verwenden, Basisklassen der API dürfen aber nicht aufgerufen werden, da dies das definierte Synchronisationsschema stört API-Einschränkung Verhindert Implementierung Applikations-definierter Threads, die direkt von niederen Thread-Abstraktionen abgeleitet sind SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 13
Probleme der Interruptsynchronisation Aus Sicht des Betriebssystementwicklers: Wenn das System durch Ableitung bereits synchronisierter Klassen erweitert wird, muss der neue Code separat synchronisiert werden Also: Lock muss freigegeben werden, bevor Methoden der synchronisierten Basisklasse aufgerufen werden Erhalt der Aufrufsematik, Vermeiden von Fehlverhalten Ergebnis: unlocked -Zeitabschnitt! In bestimmten Situationen nicht akzeptabel, egal wie kurz Ausweg: Adaption der Synchronisationsschicht Ergebnis: wieder viele aufwendige manuelle Eingriffe SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 14
Interruptsynchronisation Ergebnis: Interrupt-Synchronisation ist schwierig! Besonders das Finden von Synchronisationspunkten Keine Aufgabe für Entwickler einzelner BS-Komponenten Plazierung und Implementierung von Locks ist strategische Entscheidung Beeinflusst das gesamte System Bei sauberer Trennung der Belange: unabhägige Entscheidung Berücksichtigung globaler Systemanforderungen - Diese kann und soll ein Entwickler einer einzelnen Systemkomponente (z.b. eines Gerätetreibers) nicht kennen Gutes Beispiel: Interrupt-Latenzen Kurz halten durch feingranulare Lock-Strategie auf Kosten von Laufzeit und Code-Overhead Grobgranulare Strategie reduziert dagegen Laufzeit-Overhead - Weniger Lock-/Unlock-Aufrufe benötigt - Höhere Interrupt-Latenzen SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 15
Lösungsansatz 1 Naiver Ansatz Trennung der Implementierung der Interrupt-Synchronisations- Strategie vom Code der Komponenten Pointcuts locked : matched auf genau die Menge Methoden, die synchronisiert ausgeführt werden sollen enter und leave für einige Spezialfunktionen, die das Lock setzen oder löschen upcall beschreibt die Funktionen, die den synchronisierten Teil des Systems verlassen - Diese müssen das Lock freigeben, bevor sie den Kernel verlassen, und nachher wieder setzen SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 16
Lösungsansatz 1 Advice-Definition in Z.17 lock.enter() soll aufgerufen werden vor jedem der Joinpoints in locked oder entershould be called before any of the join points from Also bevor eine der selektierten Funktionen ausgeführt wird Advice-Definition Z. 18 Legt fest, dass lock.leave() nach den ang. Joinpoints aufgerufen werden soll Also nach Ausführung der selektierten Funktionen In Z.19-20 findet das ungekehrte statt für alle Funktionen, die den Kernel zeitweise verlassen Schwerwiegendes Problem dieser Variante Sie ist nicht robust! Nach jeder Änderung in PURE muss der Aspekt-Code geprüft werden: Sicherstellen, dass die Pointcuts immer noch alle relevanten Funktionen erfassen SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 17
Lösungsansatz 1: Implementierung SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 18
Lösungsansatz 2 Löst das Problem von Variante 1 Verwendung einer höheren Abstraktionsebene Definiert Pointcuts thread, sched, ipc, guard, driver, mm Jeder Pointcut korrespondiert mit einem Subsystem von PURE Zusätzlich: Pointcut api layer Beschreibt PURE API: alle für Programmierer verfügbare Klassen Idee: Locking-Primitive immer dann aufrufen, wenn Subsystem-Grenze eines synchronisierten Abschnitts gekreuzt wird Deutlich in Pointcut-Definition zu sehen Call Pointcut-Funktion ergibt alle Call-Joinpoints, in denen Zielfunktion ein Member der angeg. Klassen ist Hier: alle Klassen, die zu synchronisierter Kernel-Region gehören Davon nur diese auswählen, die nicht in einer Kernel-Klasse sind, also Calls von außerhalb in die synchronisierte Kernel-Region SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 19
Lösungsansatz 2: Komponenten SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 20
Lösungsansatz 2: Aspekte SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 21
Vergleich der Ansätze Codegrößen Original und Aspekt-Variante 1 & 2 PURE: BS für tief eingebettete Systeme Größe stark von Applikation abhängig 3 Test-Applikationen - friend: kooperative Threads, keine Interrupts - philo und clock: preemptives, interrupt-getriebenes Thread-Scheduling AOP-Implementierungen erhöhen Codegröße nicht! Signifikante Reduktion für Variante 2 Grund: einige Klassen, die nur zum Aufruf von Synchronisations- Primitiven verwendet wurden, konnten entfernt werden SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 22
Inhalt Aspekte in Betriebssystemen Paper: Interruptsynchronisation in PURE AspectC++-Tutorial Fortführung des Tutorials aus der Vorlesung SuS Übung 3 Aspektorientierung in Betriebssystemen und AspectC++-Tutorial 23