Hallo Leute, ich will hier einen extrem schlanken Ansatz f"ur ARM-Prozessoren im THUMB-Modus (Cortex-M0, Cortex-M3, ARM4TDMI Thumb) vorstellen, mit dem man richtiges Kontext-Umschalten zwischen Threads durchf"uhren kann. Also keine 'armer Mann' Implementierung. Kooperatives Multitasking. Ich habe es erst k"urzlich auf dem Cortex-M0 portiert, nachdem ich es auf dem ARM7TDMI schon eine Weile in Programmen (ARM-Modus) benutze. Die Threads m"ussen eine wichtige Bedingung erf"ullen: ihre 'main()' darf nie zur"uckkehren - daher der Name Thread-Loop, tl - damit man immer daran erinnert wird. tl.h sind die C-Funktionsprototypen und die Context-Datenstruktur. tl.s ist die Implementierung in Thumb2-Code. multitask.c ist ein Beispielprogramm, mit ein paar Threads und einer m"oglichen Implementierung von sleep(). Es gibt keine Synchronisationsprimitiven (mutexe, semaphore, Event-Schlangen). Auf einem M0 steht man da n"amlich ziemlich im Regen - er hat keine Befehle daf"ur. Ausserdem ist in tl gar kein Scheduler drin - das w"are mit eine zu starke Verflechtung in die Hardware. tl erlaubt eine "Anderung der Programmstruktur, es zwingt nicht zu einer totalen Ver"anderung. Ein Thread ist eine Funktion vom Typ TlLoop, d.h. eine void-Funktion, die einen (TlContext*)-Thread-Kontext als Argument hat und explizit nie zur"uckkehrt. Tats"achlich kehrt die Funktion schon immer wieder kurzzeitig zur"uck und zwar an den Stellen, an denen sie tlYield() aufruft. Sie ist sogar verpflichtet tlYield() immer wieder aufzurufen, nachdem sie eine Kleinigkeit getan hat. Ein richtiger Thread hat einen eigenen Stack. F"ur die Erzeugung dieses Stacks (RAM-Speicherbereich mit 8-bit alignment) ist der Programmierer verantwortlich. Er kann diesen sogar auf dem main()-Stack lokal anlegen. Der Stack (genauer: der initiale Stackpointer) ist in der TlContext-Struktur einzutragen. Vorsicht: der Stacks wachsen auf Cortex-M-Cores nach unten, d.h. in TlContext muss die Speicherstelle unmittelbar hinter dem f"ur den Stack reservierten Speicherbereich eingetragen werden. Ist der Stackpointer eingetragen, kann mit tlCreate() dem Thread 'Leben' eingehaucht werden. tlCreate() kehrt zum Aufrufer zur"uck, sobald die Thread-Funktion das erste mal tlYield() aufruft. Beliebig viele weitere Threads k"onnen so - mit jeweils eigenen Stacks - erzeugt werden. Ein Thread kann bis zu seinem n"achsten tlYield() weiterlaufen lassen durch einen Aufruf von tlCall() mit seinem Kontext. Welcher Thread wann laufen darf, das bleibt dem Programmierer "uberlassen. Ein Thread kann auch durch eine Interrupt-Routine weiterlaufen, nachdem er von main() mit tlCreate() erzeugt wurde und tlCreate() zu main zur"uckgekehrt ist. Es gibt keine Funktion zum Beenden eines Thread. Man ruft ihn einfach nicht mehr auf. Und wenn man ihn nicht mehr aufruft, kann man auch seinen Stack recyclen. Das Beispielprogramm multitask zeigt das alles exemplarisch. Es ist nicht f"ur eure Entwicklungsumgebungen gemacht, es basiert auf meiner c-lpc8 Bibliothek. Diese habe ich noch nicht ver"offentlicht (Jan 2014). tl ist vielmehr ein Teil derselben, f"ur den ich besonderen Ver"offentlichungsdrang versp"ure. tl ist vermutlich nicht einsetzbar unter ucLinux oder "ahnlichem, da ich das Register R12 (IP) nicht sichere, d.h. Zielplattform ist 'bare metal' aka 'freestanding'. Bitte kommt nicht auf die Idee, den wenigen Assembler-Code als inline-Assembler zu schreiben und die Funktion zu 'inlinen' - das geht m"achtig in die Hose! Keine der Funktionen darf inline sein oder (unabsichtlich) inline werden, denn sie verlassen sich alle auf den Inhalt von R14 (LR). Ja, man k"onnte TlContext auf einen einzigen Pointer reduzieren. Aber so wie's jetzt ist, sind die Aufgaben der Felder menschenverst"andlicher.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.