Entwurf und Implementierung einer Multitasking ?· Entwurf und Implementierung einer Multitasking-Umgebung…

Download Entwurf und Implementierung einer Multitasking ?· Entwurf und Implementierung einer Multitasking-Umgebung…

Post on 14-Jul-2018

212 views

Category:

Documents

0 download

Embed Size (px)

TRANSCRIPT

<ul><li><p>Entwurf und Implementierung einerMultitasking-Umgebung fr den Arduino</p><p>Due</p><p>Bachelorarbeit</p><p>InfB-BA/Inf Abschlussmodul B.Sc. Informatik</p><p>Autor: Enno Kster 5953573Erstgutachter: Norman HendrichZweitgutachter: Kai JanderZeitraum:Beginn: 16.12.2015, Abgabe: 3.6.2015Universitt Hamburg</p><p>Fakultt fr Mathematik, Informatik und NaturwissenschaftenFachbereich InformatikBachelor of Science Informatik</p><p>Juni 2015</p></li><li><p>Inhaltsverzeichnis1 Einleitung 1</p><p>2 Grundlagen 22.1 Begriffsdefinitionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.2 Scheduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4</p><p>2.2.1 Zeitpunkt eines Kontextwechsels . . . . . . . . . . . . . . . . . . . 42.2.2 Reihenfolge der Ausfhrung . . . . . . . . . . . . . . . . . . . . . 5</p><p>2.3 Notwendigkeiten beim Einsatz mehrerer Tasks . . . . . . . . . . . . . . . 62.3.1 Interprozess-Kommunikation . . . . . . . . . . . . . . . . . . . . . 62.3.2 Threadsicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . 7</p><p>3 bersicht bestehender Projekte 83.1 Timing Bibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.2 Vollstndige Scheduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10</p><p>3.2.1 bersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.2.2 Einzelbetrachtungen . . . . . . . . . . . . . . . . . . . . . . . . . 113.2.3 Auswahl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13</p><p>4 Integration mit der Arduino-Bibliothek 144.1 Probleme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144.2 Lsungsmglichkeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15</p><p>4.2.1 Arduino-Bibliothek erweitern . . . . . . . . . . . . . . . . . . . . 154.2.2 Arduino-Funktionen ersetzen . . . . . . . . . . . . . . . . . . . . . 154.2.3 Nutzer verantwortlich machen . . . . . . . . . . . . . . . . . . . . 17</p><p>4.3 Einzelbetrachtungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184.3.1 malloc()-Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . 184.3.2 delay()- und yield()-Funktionen . . . . . . . . . . . . . . . . . . . 214.3.3 Serial-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23</p><p>5 Fazit 25</p><p>6 Anhang 26</p><p>i</p></li><li><p>1 EinleitungDer Name Arduino steht fr die einfache, gnstige, plattformbergreifende Entwicklungvon eingebetteten Systemen auf Basis von offener Hardware und Software.Bei Systemen mit geringer Leistung und sehr beschrnkten Ressourcen wie den AVR-</p><p>Prozessor basierten alten Arduinos werden meist nur wenige oder eine einzige Aufgabebearbeitet. Wie in allen anderen Bereichen wird auch die Leistung eingebetteter Systemestetig grer. Der Arduino Due erhht im Vergleich zu seinen Vorgngermodellen dieTaktrate des Prozessors um das sechfache und die Gre des Arbeitsspeichers um das48-fache. Durch diese Leistungssteigerung ist der Gedanke naheliegend ein System freine Vielzahl von Aufgaben zu nutzen.Mehrere Aufgaben ganz einfach in einem Kontrollfluss (Task) zu erledigen macht den</p><p>Quelltext recht schnell unbersichtlich, insbesondere wenn hufig zwischen den Aufga-ben gewechselt werden muss. Die Entwicklung wird fehleranfllig und kompliziert. Demkann man entgegenwirken, indem man logisch trennbare Teile in verschiedene Tasksaufteilt. Die einzelnen Tasks mssen sich den Prozessor teilen. Damit sie ihre Funktionerfllen knnen, mssen sie abwechselnd auf dem Prozessor ausgefhrt werden. Um dieszu gewhrleisten, muss es ein System, genannt Scheduler, geben, dass die Verteilung derRechenzeit kontrolliert.Diese Arbeit zielt auf ein mglichst einfach zu nutzendes System im Geiste der Ardui-</p><p>nophilosophie ab. Es soll mit wenigen Befehlen die Implementation mehrerer nebenein-ander ausgefhrter Funktionen ermglichen, sodass auch Einsteiger schnell und ohne vielVorwissen anfangen knnen das System zu nutzen. Die Hauptzielplattform ist der Ar-duino Due. Die Untersttzung lterer Arduino Systeme besitzt allerdings ebenfalls einenhohen Stellenwert, weil sie die Lsung einer greren Zahl von Entwicklern zugnglichmacht.Mit der Ausfhrung mehrerer Funktionen nebeneinander ist es allerdings nicht ge-</p><p>tan. Durch den Einsatz mehrerer Tasks ergeben sich zustzliche Anforderungen an dieArduino-Bibliothek, welche Anpassungen notwendig machen. Die Funktionalitten derBibliothek und des Multitaskingsystems berschneiden sich. Es soll anhand einiger Bei-spiele aufgezeigt werden, wie diese Konflikte gelst werden knnen.Auch auf den Nutzer des Multitaskingsystems kommen zustzliche Anforderungen</p><p>zu. Zum Beispiel knnen Abhngigkeiten zwischen den verschiedenen Teilen seines Pro-gramms bestehen, die eine Kommunikation und/oder Synchronisation erfordern. DieseProbleme muss der Nutzer selbst lsen. Das Multitaskingsystem kann ihm dabei al-lerdings mit einigen zustzlichen Funktionalitten unter die Arme greifen. Neben derHauptaufgabe des Schedulings soll das System nach Mglichkeit auch fr die Kommu-nikation und Synchronisation Funktionen bereithalten.</p><p>1</p></li><li><p>2 Grundlagen</p><p>2.1 BegriffsdefinitionenIn der Literatur sind viele der in dieser Arbeit genutzten Grundbegriffe verschiedendefiniert oder aber gleiche Begriffe meinen unterschiedliche Dinge. Dieses kurze Kapitelsoll Missverstndnissen vorbeugen, indem die wichtigsten Ausdrcke festgelegt werden.Ihre Definitionen basieren auf denen aus Moderne Betriebssysteme[Tan09] von AndrewS. Tanenbaum.</p><p>Prozesse, Threads und Tasks</p><p>Ein Prozess ist die Instanz eines Programms in Ausfhrung, inklusive des aktuellenWertes des Befehlszhlers, der Registerinhalte und der Belegungen der Variablen. Kon-zeptionell besitzt jeder Prozess seine eigene virtuelle CPU. Jeder Prozess hat seineneigenen Speicherbereich, der vor Zugriffen durch andere Prozesse geschtzt ist. Er kanndrei Zustnde haben, rechnend(die Befehle werden in diesem Moment auf der CPU aus-gefhrt), rechenbereit(kurzzeitig gestoppt, um einen anderen Prozess rechnen zu lassen)und blockiert(nicht lauffhig bis ein bestimmtes externes Ereignis eintritt). [Tan09]In traditionellen Betriebssystemen hat jeder Prozess einen eigenen Adressraum und</p><p>einen einzigen Ausfhrungsfaden(thread of control). [Tan09] Threads sind also einemProzess untergeordnet. Sie besitzen einen eigenen Befehlszhler und Registerinhalte. Siehaben Zugriff auf auf den kompletten Speicherbereich des Prozesses. Die Belegungenvon Variablen sind abhngig von der Art ihrer Allokation zwischen den Threads geteiltoder unabhngig voneinander. Mehr dazu unter Stack und Heap.</p><p>Exkurs Arduino: Die Prozessoren lterer Arduino-Boards besitzen keine hardware-seitige Untersttzung fr Speicherschutz, um die Trennung der Speicherbereiche verschie-dener Prozesse zu erzwingen. Erst der Arduino Due hat eine Memory-Protection-Unit.Entsprechend ist die softwareseitige Untersttzung des Prozesskonzepts ebenfalls in denmeisten Systemen non-existent, in Zukunft aber vielleicht realisierbar. Im folgenden solldeswegen nur noch die Rede von Tasks sein, wenn Aussagen sowohl auf Prozesse alsauch auf Threads zutreffen, und angenommen werden, dass damit immer Threads mitgeteiltem Speicher gemeint sind. Falls fr das Thema wichtige Unterschiede bestehen,werden diese erwhnt.</p><p>3</p></li><li><p>Stack und Heap</p><p>Der Speicher wird blicherweise in drei Teile aufgeteilt, den Stack, den Heap und einenBereich fr globale Variablen. Wenn der Arduino regulr mit einem einzelnen Task pro-grammiert wird, ist die Unterscheidung der Abschnitte grtenteils unerheblich, da derOrt und die Gre des Stacks und des globalen Bereichs vorher bekannt sind. Sobaldallerdings mehrere Tasks laufen sollen, muss die Speicherverwaltung sich auch um diedynamisch erzeugten Stacks kmmern.Fr jeden Task wird bei seinem Start eine gewisse feste Menge Speicher fr seinen</p><p>Stack voralloziert. Der Stack enthlt Rcksprungadressen fr Funktionen und alle lokalenVariablen in Funktionen, die normal (z.B. int a=0;) deklariert werden. Zustzlich kannein Task z.b. ber die Funktion malloc() in C oder den operator new in C++ dynamischzustzlichen Speicher allozieren. In den globalen Bereich fallen zum einen Variablen,die auerhalb aller Funktionen deklariert sind, und zum anderen als static deklarierteVariablen, die zwar innerhalb einer Funktion deklariert werden, aber trotzdem globalsind.</p><p>Multitasking</p><p>Multitasking bedeutet die Ausfhrung mehrerer Tasks auf einem Prozessor. Fr einefehlerfreie Funktion muss klar definiert sein, wie ein laufender Task die Kontrolle abgibt,wie ein anderer sie wieder aufnimmt und welcher der gestarteten Tasks als nchstes ander Reihe ist. Die Bestimmung der Reihenfolge ist Aufgabe des Schedulers, der weiterunten beschrieben wird.Ein Wechsel der Kontrolle von einem Task zum anderen wird Kontextwechsel genannt.</p><p>Als Kontext wird hier der Inhalt der Register betrachtet. Es wird zunchst der Kontextdes abgebenden Tasks im Speicher gesichert. Dann bestimmt der Scheduler, welcher Taskals nchstes ausgefhrt werden soll. Danach werden die zu einem frheren Zeitpunktgesicherten Kontextinformationen des folgenden Tasks wiederhergestellt.</p><p>Interrupts</p><p>Interrupts sind, wie der Name schon suggeriert, Unterbrechungen des normalen Kon-trollflusses. Diese knnen von verschiedenen Ereignissen ausgelst werden.Das wichtigste fr diese Arbeit ist der Ablauf eines Timers, denn darauf basieren die</p><p>spter beschriebenen preemptiven Scheduler. Fehler im derzeit ausgefhrten Programmwie zum Beispiel unzulssige Speicherzugriffe oder Berechnungsfehler knnen ebenfallsInterrupts auslsen. Auerdem knnen im Bereich der eingebetten Systeme besonderswichtige uere Ereignisse, wie Flankenwechsel an digitalen Pins des Prozessors, dennormalen Ablauf unterbrechen.Wenn ein Interrupt ausgelst wird, arbeitet der Prozessor eine vorher fr dieses Er-</p><p>eignis festgelegte Prozedur ab. Die Adresse dieser Prozedur ist in einer Tabelle abgelegt,die Interrupt Vector genannt wird.</p><p>4</p></li><li><p>2.2 Scheduler</p><p>Der Scheduler ist das zentrale Element des Systems. Er bestimmt, welcher Task alsnchstes ausgefhrt wird.Scheduler lassen sich anhand zwei verschiedener Merkmale voneinander unterscheiden</p><p>und in Kategorien einordnen. Das erste ist der Zeitpunkt eines Kontextwechsels, welcherentweder vom gerade ausgefhrten Task selbst gewhlt oder vom System vorgegebenwerden kann. Das zweite Merkmal ist die Reihenfolge und Hufigkeit, in der die Tasksausgefhrt werden.</p><p>2.2.1 Zeitpunkt eines Kontextwechsels</p><p>Kooperative Scheduler</p><p>Dieser Schedulertyp zeichnet sich dadurch aus, dass die Kontrolle vom aktuell laufendenTask selbststndig an den Scheduler zurckgegeben wird. Ein Kontextwechsel passiertnur, wenn der Task eine in den meisten Systemen yield()(englisch: hergeben) genannteFunktion aufruft, welche den Task unterbricht und den Scheduler aufruft.Kooperative haben gegenber preemptiven Schedulern einige kleine Vorteile. Diese</p><p>werden jedoch durch einen signifikanten Nachteil erkauft.Ein Vorteil dieses Ansatzes ist, dass im Gegensatz zu dem spter erklrten preemptiven</p><p>Ansatz keine hardwareseitige Untersttzung fr Timer-Interrupts bentigt wird. AlleArduino Systeme haben diese Untersttzung. Daher ist dieser Punkt hchstens in demFall von Interesse, dass man eine Portierung des Systems auf andere Prozessoren plant,welche keine Timer-Hardware besitzen.Auerdem spricht im Falle von sehr wenigen Kontextwechseln der geringe Verlust an</p><p>Rechenzeit fr das Scheduling fr kooperative Scheduler. Dies fllt allerdings nur beisehr niedrig getakteten Prozessoren ins Gewicht.Ein weiterer Pluspunkt ist, dass der Programmierer selbst bestimmt, wann ein Task</p><p>unterbrochen wird. Wenn der Zeitpunkt bestimmter Vorgnge innerhalb einer Sequenzbesonders wichtig ist, um zum Beispiel die Spezifikationen eines Protokolls exakt ein-halten zu knnen, dann ist dies ganz einfach dadaurch zu gewhrleisten, dass man imbetreffenden Task die Kontrolle nicht abgibt bevor die komplette Sequenz durchlaufenist. Beim preemptiven Ansatz muss man zustzliche Manahmen ergreifen, um dies zugewhrleisten.Der groe Nachteil des kooperativen Ansatzes ist allerdings genau dieses mgliche</p><p>Verhalten eines Tasks, den Prozessor fr lngere Zeit belegen zu knnen. Dies kann durcheinen Programmierfehler auch unabsichtlich zu beliebig langen Wartezeiten fhren. EinFehler in einem Task kann das ganze System gefhrden. Der Programmierer muss selbstdafr Sorge tragen, dass keiner seiner Tasks den Prozessor zu lange belegt und damiteventuell andere Tasks in ihrer Funktion behindert.</p><p>5</p></li><li><p>Preemptive Scheduler</p><p>Dieser Schedulertyp wartet nicht wie kooperative Scheduler darauf, dass ein Task frei-willig den Prozessor freigibt. Dies ist zwar generell mglich, aber nicht notwendig. Einpreemptiver Scheduler erzwingt die Unterbrechung der Ausfhrung eines Tasks nacheinem festen Intervall. Ein Timer lst periodisch einen Interrupt aus, woraufhin derScheduler seine Arbeit aufnimmt.Der groe Vorteil preemptiver Scheduler im Vergleich zu ihren kooperativen Pendants</p><p>ist, dass sie ihren groen Nachteil korrigieren. Ein Task kann nicht alle anderen blockie-ren, weil ihm nach Ablauf seines Intervalls garantiert die Kontrolle ber den Prozessorentzogen wird.Erkauft wird das durch die entsprechenden kleinen Nachteile.Wenn der Scheduler zum Beispiel auf ein Intervall von einer Millisekunde eingestellt</p><p>ist, dann wird 1000 mal pro Sekunde ein Kontextwechsel vorgenommen, ganz egal, obdas aus Sicht der beteiligten Tasks notwendig ist oder nicht.Wenn man einen Ablauf im Programm hat, dessen Elemente zwingend zueinander</p><p>zeitnah abgearbeitet werden mssen, dann muss dieser Teil als sogenannter kritischerBereich markiert werden, um eine Unterbrechung durch den Scheduler zu unterbinden.</p><p>2.2.2 Reihenfolge der AusfhrungRound Robin</p><p>Die Grundidee dieser Variante ist sehr einfach. Der Prozessor, soll den Tasks zu gleichenTeilen zur Verfgung gestellt werden. In der Frage , wie gleich definiert sein soll,unterscheiden sich die Implementationen.Eine Mglichkeit ist, dass man einfach die Tasks der Reihe nach einmal ausfhren</p><p>lsst. Beim kooperativen Ansatz, wrde das bedeuten, dass Tasks, die erst nach lngererZeit die Kontrolle zurckgeben, gleich oft laufen aber mehr Zeit bekommen wrden.Beim preemptiven Ansatz tritt dieses Problem nicht auf, da jeder Task sptestens nachAblauf des vom Scheduler festgelegten Intervalls unterbrochen wird.Der Ansatz, das Ausfhrungsrecht reihum zu verteilen, kann noch etwas verfeinert</p><p>werden, indem man es ermglicht den Tasks eine Prioritt zuzuordnen. Je nach Systemknnen diese verschieden genutzt werden. In Desktop-Systemen werden hher priorisierteTasks einfach hufiger ausgefhrt. In Echtzeitbetriebssystemen hat die hhere Priorittblicherweise immer Vorrang. Mehrere niedrig priorisierte Tasks wechseln sich nur so-lange untereinander ab, bis ein Task hherer Prioritt lauffhig wird. Dieser belegt denProzessor dann solange, bis er mit seiner Arbeit fertig ist. [Tha12]</p><p>Deadline</p><p>Bei dieser Schedulerart bestimmt die am nchsten liegende Deadline der Tasks, welchervon ihnen als nchstes ausgefhrt werden soll. Die verschiedenen Implementationen un-terscheiden sich darin, wie diese die Deadline bestimmen. blicherweise wird diese beiErstellung de...</p></li></ul>

Recommended

View more >