Hallo, derzeit bin ich dabei einen Task Manager für meine Mikrocontroller Applikation zu entwickeln. Was die Implementierung betrifft habe ich noch nicht so ganz den durchblick wie ich swas realisieren könnte. Es gibt insgesamt 6 Tasks. Diese sollen angelegt und initialisiert werden. Sobald zum Beispiel ein gültiges UART Telegramm empfangen wird, soll in eine Queue ein Task angemeldet werden. Wie kann man sowas in der Art implementieren?
Ich würde mal die Doku von FreeRTOS anschauen. Das ist vermutlich vernünftiger, als das Rad neu zu erfinden.
Ok FreeRTOS. Das werde ich in der Entwicklung nicht durchbekommen. Das habe ich auch mal angesprochen aber da kommt immer eine aussage wir brauchen ja was ganz einfaches. Macht es wirklich keinen Sinn das alles selber zu implementieren?
Dann suche mal nach * Zustandsautomat * Endlicher Automat * State Machine (ist alles das selbe) Mehrere Zustandsautomaten lassen sich relativ einfach quasi parallel ausführen. http://stefanfrings.de/multithreading_arduino/index.html
Danke Steve für den Link. Jetzt stellt sich die Frage welches Modell für mich geeignet wäre?
Jo schrieb: > Jetzt stellt sich die Frage welches Modell für mich geeignet wäre? Was meinst du mit "welches Modell"?
Jo schrieb: > Ein Zustandsautomaten oder mehrere Zustandsautomaten. Wenn du mehrere Sachen parallel abarbeiten musst, brauchst du mehrere. Wenn du deine Ereignisse nacheinander abarbeiten kannst, brauchst du vielleicht nur einen. Es kommt drauf an, wie viele "Dinge" dein Programm steuert, die einen eigenen Status haben. In dem Beispiel mit den drei Blinkenden LEDs hat braucht jede LED ihren eigenen Status, weil sie unabhängig voneinander blinken sollen.
Jo schrieb: > Sobald zum Beispiel ein gültiges UART Telegramm empfangen wird, > soll in eine Queue ein Task angemeldet werden. Wozu soll das gut sein? Du läßt den UART-Interrupt alle Zeichen in einen FIFO schreiben. In der Mainloop testest Du, ob ein Zeichen im FIFO ist und schreibst es in einen Befehlspuffer. Dann prüfst Du, ob ein gültiges Paket fertig ist und gibst es an den Parser, der er es dann ausführt. Angemeldet werden muß da nichts, d.h. Du bist ständig empfangsbereit. Und wenn nichts reinkommt, tust Du eben auch nichts.
Jo schrieb: > Ok FreeRTOS. ... > Macht es wirklich keinen Sinn das alles selber zu implementieren? Kommt immer darauf an. Je nachdem was du alles benötigst, hast du am ende ein komplettes OS selbst entwickelt. Da steckt dann schon ggf. eine Menge an Arbeit drin, die vorallem auch erstmal getestet und später gewartet werden will. Du solltest dir erstmal genauer überlegen, was du wirklich benötigst. Wie eigenständig muss jeder Task agieren können? Das was du da machen willst nennt sich eher "Prozess-Scheduler" und nicht "Manager". 1.) Reicht ein einfaches Modell wo jeder Task dafür verantwortlich ist das wenn es seine Arbeit gemacht hat zum Hauptprogramm (alias main) zurückgekehrt wird und dieses dann nachschaut was anliegt und die Arbeit verteilt => Cooperatives multitasking 2.) Oder aber muss ein Task auch "gegen seinen Willen" unterbrochen werden können, damit anderen Task in einem bestimmten Zeitraster auch gesichert Rechenzeit bekommen? Dann wird es schon erheblich aufwendiger, weil du ja den zustand der unterbrochene Task speichern musst und beim Fortsetzen wieder herstellen musst usw. => Preemptive multitasking 3.) Nicht zu verwechseln ist das ganze mit Interupts. Mit etwas geschick kann man mit denen die Hardware Ereignisse entgegennehmen, diese zwischenspeichern, ggf. flags setzen und in der main damit dann Steuern welcher Programmteil die zwischengespeicherte Daten weiterverarbeiten soll. => state machine/zustandsautomat. Das ist auf einfachen µC, gerade wenn du keine OS verwenden willst, am ehesten der weg wenn mehr als eine Aufgabe erledigt werden soll. Spätestens beim zweiten bist du schon über so Hilfen wie RTOS hinaus. Müssen die einzelnen Programme am ende auch noch unabhängig voneinander Erstellt werden können und es ist nicht nur ein Programm das mehrere Unterprogramme hat, bist du bei MMU und ähnlichem. Und wenn dann auch noch unterschiedliche Prioritäten der Task und sowas mit ins Spiel kommen bist und bei Linux oder so angekommen. Und das mal eben selbst zu implementieren, kannst du dir selbst ausrechen... Und wenn die Prozesse untereinander und unabhängig voneinander auch noch munter Kommunizieren sollen bist du dann bei Pipe und Message-Queues usw. Von irgendwelchen austauschbaren Hardware-Treiber von Drittanbietern mit deren ganzen Abstraktionsschichten, die teilweise weit über eine einfache HAL hinausgehen können, geht es dann irgendwo weiter. Nur mal um sich etwas in die Begrifflichkeiten einzuarbeiten. Für Details - da sind schon viele Promotionen drüber geschrieben worden:-) - https://de.wikipedia.org/wiki/Prozess-Scheduler - https://en.wikipedia.org/wiki/Computer_multitasking - https://en.wikipedia.org/wiki/Cooperative_multitasking - https://en.wikipedia.org/wiki/Preemption_(computing)#Preemptive_multitasking - https://en.wikipedia.org/wiki/Context_switch - https://de.wikipedia.org/wiki/Pipe_(Informatik) - https://en.wikipedia.org/wiki/Operating_system Daher, erst noch mal sehr genau überlegen was wirklich genau gebraucht wird, und nicht was man vielleicht noch alles machen könnte. Und wenn man das dann genauer weiß, schauen was gibt es schon, z.B. RTOS bzw. wie groß ist der Aufwand das alles selbst zu erstellen. Und aufpassen das amn auch an alles denkt, gerade das nachträgliche einbauen von weiteren Funktionen kann richtig schön Zeit fressen, wenn man es in Design nicht von anfang an berücksichtigt hat. Das sind aber alles Fragen die nur du beantworten kannst, weil nur du die genauen Anforderungen kennst (hoffentlich). Eine Universalantwort, mach einfach xyz - damit geht immer alles, gibt es hier nicht.
Jo schrieb: > Es gibt insgesamt 6 Tasks. Diese sollen angelegt und initialisiert > werden. Sobald zum Beispiel ein gültiges UART Telegramm empfangen wird, > soll in eine Queue ein Task angemeldet werden. Wie kann man sowas in der > Art implementieren? Keep it simple, also kooperativ, wenn nur irgend möglich. Aber warum eine Queue und nicht einfach Round-Robin: Müssen bestimmte Tasks vor anderen Priorität haben? Kannst du nicht stattdessen die maximale Ablaufzeit der atomaren Verarbeitungsschritte deiner Tasks so verkleinern dass alle Tasks mit der maximalen Latenz klarkommen? LG, Sebastian
Jo schrieb: > Ein Zustandsautomaten oder mehrere Zustandsautomaten. So viele, wie du brauchst, um die zu erledigende Arbeit in so kleine Schritte aufzuteilen, dass 1. nirgends mehr ein delay_ms() steht und 2. die Hauptschleife schnell genug duchlaufen wird. Und das ist jetzt der eigentliche Witz: die Hauptschleife muss ständig durchlaufen werden. Es wird nirgendwo auf irgendwas "gewartet", sondern nur geprüft, ob Flags gesetzt sind oder Zeiten erreicht wurden. Wenn es nichts zu tun gibt, dann dauern diese Abfragen und damit ein Hauptschleifendurchlauf nur ein paar µs. Wenn es viel zu tun gibt, dann kann der auch mal länger dauern. Und wenn der mal länger als 10ms dauert, dann muss der Arbeitsschritt, der Zeit frisst, in kleinere Schritte aufgeteilt werden. Ich setze mitr z.B. eine Grenze für die maximale Zykluszeit der Hauptschleife von 10ms. Mit 10ms kannst du noch hinreichend schnell auf Ereignisse von "aussen" reagieren und das System fühlt sich "schnell" an. Wenn dann aber z.B. ein Display zu beschreiben ist und das Schreiben 50ms dauern würde, dann teile ich diesen Vorgang so in 10 Schritte auf, dass das Display nacheinander in 10 Hauptschleifendurchläufen geschrieben wird. So komme ich mit der maximalen Zykluszeit wieder unter 10ms.
:
Bearbeitet durch Moderator
Ich danke euch für die zahlreichen Beiträge. Ein OS selber für meine Bedürfnisse zu entwickeln werde ich nicht. Darin liegen nicht meine Kompetenzen.
Nunja... für sowas braucht es nicht unbedingt Kompetenz, im aller einfachsten Fall sieht das so aus:
1 | void loop() { |
2 | if(Statemaschine % 10 == 0) // alle 10ms |
3 | {
|
4 | t++; |
5 | if(t>=(NUM_PIXELS*3)) t=0; |
6 | pattern_table[WSProgramm].pat(NUM_PIXELS, t,bright); |
7 | }
|
8 | else if(Statemaschine % 2000 == 0) // alle 2s |
9 | {
|
10 | Serial.printf("Statuscode: %s\n",Draconix.SCode); |
11 | }
|
12 | }
|
Dazu einfach in einem Timerinterrupt deines geringsten Misstrauens alle 1ms die Variable "Statemaschine" (uint16_t) um eines erhöhen. Den Überlauf macht sie ja eh selber.
Das was du als Taskmanager bezeichnest ist wohl eher ein https://de.m.wikipedia.org/wiki/Prozess-Scheduler
Jo schrieb: > derzeit bin ich dabei einen Task Manager für meine Mikrocontroller > Applikation zu entwickeln. Jo schrieb: > Ein OS selber für meine Bedürfnisse zu entwickeln werde ich nicht. Ich könnte dir meine kooperativen Tasks überlassen. Basieren auf den Protothreads des Adam Dunkels. Sind allerdings noch schlichter, hat z.B. keine Taskcontrolblocks irgendwelcher Art.
Jo schrieb: > Ein OS selber für meine Bedürfnisse zu entwickeln werde ich nicht. Scheduler gibts doch hier Beitrag "Wartezeiten effektiv (Scheduler)" Jo schrieb: > meine Mikrocontroller > Applikation streng geheim der verwendete µC?
Jo schrieb: > Ok FreeRTOS. Das werde ich in der Entwicklung nicht durchbekommen. > Das habe ich auch mal angesprochen aber da kommt immer eine aussage wir > brauchen ja was ganz einfaches. FreeRTOS ist extrem konfigurierbar, und hat ggf. einen sehr kleinen Footprint. Was halbwegs Vergleichbares selbst kleiner hinzubekommen halte ich schon für sehr ambitioniert. Ausserdem hast du auch die Wahl zwischen kooperativen und preemptiven Multitasking. Ich würde mir das auf jeden Fall nochmal genauer anschauen, bevor du das Rad neu erfindest.
Rene K. schrieb: > if(Statemaschine % 10 == 0) // alle 10ms > else if(Statemaschine % 2000 == 0) // alle 2s Wenn dieser Code die richtige Millisekunde verpasst, weil mal etwas länger gedauert hat, dann wird der Schritt ausgelassen. Außerdem funktionieren die Intervalle nicht korrekt, wenn der Zähler überläuft. So macht es nicht. Es gibt reichlich Artikel im Internet, die gut funktionierende Vorschläge machen.
EAF schrieb: > Basieren auf den Protothreads des Adam Dunkels. Kann ich nur von abraten. Die Protothreads verstecken switch/case Statements hinter Makros, um den Ablauf angeblich übersichtlicher zu gestalten. Dafür stimmt dann aber der scheinbare Scope von Variablen nicht mehr mit dem überein, was tatsächlich passiert. Weder Compiler noch Linker können entsprechende Fehler erkennen. Damit schießt man sich ganz schnell ins eigene Knie.
Beitrag #7336827 wurde von einem Moderator gelöscht.
Jo schrieb: > Sobald zum Beispiel ein gültiges UART Telegramm empfangen wird, soll in > eine Queue ein Task angemeldet werden. Wie kann man sowas in der Art > implementieren? Du willst also ein event-gesteuertes System, vermutlich ohne präemptives Multitasking. Also so was wie Windows 16. Jeder Event hängt an einem Interrupt und fügt einen Eintrag zu den wartenden tasks hinzu. Die while Hauptschleife in main prüft ständig ob ein task wartet und ruft den dann auf uartsend(ch) if(uart.busy) uart.sendqueue.push(ch); else uart.tx=ch; uartempty(void) if(!uart.sendqueue.empty()) uart.tx=uart.sendqueue.pop(); void interrupt uartTX() addtask(uartempty); addtask(calladdress) waitqueue.push(calladdress); main: while(1) yield(); yield(void) if(!waitqueue.empty()) (*waitqueue.pop())(); Windows schleppt mehr Daten rum, inklusive der genauen Zeit des events. Du kannst das ergänzen. Statt einer queue kann man auch für die 4-5 events je nur ein flag setzen.
:
Bearbeitet durch User
Steve van de Grens schrieb: > Die Protothreads verstecken switch/case > Statements hinter Makros, Das ist ein Irrtum! Lokale Variablen leben nicht lange, das ist richtig. Da das "System" stackless arbeitet. Der erste Vorteil auf keinen µC Steve van de Grens schrieb: > übersichtlicher zu gestalten Da ist der zweite Vorteil "Du" musst es ja nicht nutzen.
Steve van de Grens schrieb: >> [Protothreads] > Kann ich nur von abraten. 100% Zustimmung. EAF schrieb: >> Die Protothreads verstecken switch/case Statements hinter Makros, > >Das ist ein Irrtum! Nein, genau das machen sie. Und das auf eine so primitive Art und Weise, dass man deren Aufbau genau kennen muß, um sie korrekt einsetzen zu können. Das ist eine hauchdünne Maske, um den Anschein von Threads zu erwecken, aber bei den geringsten Ansprüchen fällt alles auseinander. Steve schrieb ja schon, dass man keine lokalen Variablen benutzen kann, man kann auch nur in der Top-Level-Funktion eines "Protothreads" warten. Das macht das Ganze zu einem kuriosen Spielzeug - triviale Beispiele sehen ... ok ... aus, alles andere wird komplizierter oder ist unmöglich. Btw, anhand der Fragen des TO habe ich den Eindruck, dass er sich auf dem Stand von 1/2 bis 1 Jahr Schulinformatik befindet. Das einzige, was er aus diesem "Thread" ziehen dürfte, ist der (mMn nicht verkehrte) Eindruck, dass er der ihm gestellten Aufgabe noch nicht ganz gewachsen ist ;-)
foobar schrieb: > Nein, genau das machen sie. No! Mein Protothread Abkömmling nutzt kein switch/case im verborgenen!
Jo schrieb: > Ein OS selber für meine Bedürfnisse zu entwickeln werde ich nicht. Darin > liegen nicht meine Kompetenzen. Welchen Prozessor oder Plattform nutzt Du? Free-RTOS ist etwas Jo schrieb: > ganz einfaches Wenn Du einen Tiny hast und nur wenig RTOS-Features brauchst, gibt es evt. noch einfacheres. Oder plattformspezifisched. Es gibt 2 beliebte Fehler: 1. Der Geschmack kommt beim Essen. Man bastelt selbst nur (z.B. Scheduler) und baut frickelt später alles andere (z.B. Queues) nach. 2. Man fühlt sich gezwungen, ALLE Features zu verwenden. Oft macht es Sinn, einige Sachen weiterhin im Interrupt oder mit eigenen Puffern zu machen, statt mit (nicht geeigneten) Featuren des RTOS.
EAF schrieb: > Mein Protothread Abkömmling nutzt kein switch/case im verborgenen! Dann reden wir wohl von unterschiedlichen Dingen. Ich meine die Protothreads von Adam Dunkels, die er dort dokumentiert hat: http://dunkels.com/adam/pt/ Ich habe mal die aktuelle lc-switch.h Datei angehängt. Darin befinden sich die Makros, welche die switch/case Konstrukte erzeugen. EAF, welche Protothreads meinst du denn? Ich würde sie mir gerne mal anschauen.
Steve van de Grens schrieb: > Ich würde sie mir gerne mal anschauen. Hää... Ich soll dir, Nörgler und Schreihals, Munition liefern? Steve van de Grens schrieb: > EAF, welche Protothreads meinst du denn? Ich meine schon dem Adam seine! Darauf basiert auch meine Konstruktion. Ist etwas schlanker, da keine Taskcontrolblocks verwendet werden. Ist C++. Also OOP. Eine Task == eine Instanz Von Arduino wird millis() für Zeitsteuerungen verwendet, um einen nebenläufigen delay() Ersatz zu schaffen. Da kein Stack, bei einem Taskwechsel, erhalten bleibt, finden ansonsten statische/lokale Variablen ihren Platz als Instanzeigenschaften. Das Ganze ist eine Library im Arduino Stil Ein paar Beispiel sind dabei. Allesamt einfache Automaten. Wie auch immer, wenn du dem Dunkels seinen Kram nicht leiden kannst, wird es mir hier wohl nicht besser ergehen...... Also los: Kotz dich aus.
EAF schrieb: > Ich soll dir, Nörgler und Schreihals, Munition liefern? Darum geht es nicht. Ich finde die Protothreads (die ich kenne) prinzipiell für eine feine Sache. Aber sie bringen wegen dieser switch/case Konstrukte zu viele neue Probleme mit sich, auf die ich höllisch aufpassen muss. Nun hast du von einer Protothread Variante geschrieben, die nicht auf switch/case aufbaut. Die würde ich mir gerne anschauen, wenn vielleicht hat man da genau die Probleme gelöst, die ich gerne loswerden möchte. Was du da in C++ angehängt hast, sieht völlig anders aus, als die mir bekannten Protothreads in C. > Wie auch immer, wenn du dem Dunkels seinen Kram nicht leiden kannst, > wird es mir hier wohl nicht besser ergehen. In diesem Fall ist C++ der Showstopper, das passt nicht in die vorhandenen Projekte. Aber ich finde es dennoch interessant. Schaue ich mir mal am Wochenende an, wenn ich die Ruhe dazu habe. Dankeschön!
Joachim B. schrieb: > streng geheim der verwendete µC? er antwortet einfach nicht, also wieder ein Trollthread.
Joachim B. schrieb: > er antwortet einfach nicht, also wieder ein Trollthread. Ich denke eher, er bekommt von all dem hier nichts mit, weil er sich nicht angemeldet hat.
Steve van de Grens schrieb: > Was du da in C++ angehängt hast, sieht völlig anders aus, als die mir > bekannten Protothreads in C. Alles nur Fassade und der Bequemlichkeit dienlich. Die Probleme bleiben die gleichen, nur das man so switch/case innerhalb der Automaten nutzen kann/darf. Die Makros sollten so ähnlich auch in C nutzbar sein. Zumindest der Gcc kann das.
Joachim B. schrieb: > wieder ein Trollthread ...aber ein interessanter. Das nutze ich schamlos für eine Zwischenfrage: EAF schrieb: > Ich könnte dir meine kooperativen Tasks überlassen. > Basieren auf den Protothreads des Adam Dunkels. Kann man denn auch mit diesen proto-threads Strom sparen? Mit preemptivem Multitasking geht es auf ganz natürliche Art. Kooperativ sollte es eigentlich auch noch funktionieren - dachte ich bis gerade eben. Aber die Protothreads müssen doch ständig diverse Flags und Zustände pollen?
:
Bearbeitet durch User
Bauform B. schrieb: > Kooperativ sollte es eigentlich auch noch funktionieren Denke schon, wenn man die dazu notwendigen Strukturen schafft. Bauform B. schrieb: > Aber die Protothreads müssen doch ständig diverse Flags und > Zustände pollen? Im Prinzip schon. In meinen Beispielen sind das an die 100000 Durchkäufe pro Sekunde(AVR 16MHz). Da könnte man durchaus Schlafetappen unterbringen. Wenn du genug Speicher für FreeRTOS o.ä, hast, nutze es.
Bauform B. schrieb: > Kann man denn auch mit diesen proto-threads Strom sparen? Ja, indem man die Hauptschleife nach erledigter Arbeit anhält, bis die nächste 10 ms Marke erreicht ist. Der Haken ist: Immer wenn der Rechner nichts zu tun hat, legt er sich bis zu 10 ms schlafen. Man muss sich halt überlegen, ob das zur Anwendung passt. Es geht auch mit kleineren Intervallen.
Steve van de Grens schrieb: > Bauform B. schrieb: >> Kann man denn auch mit diesen proto-threads Strom sparen? > > Ja, indem man die Hauptschleife nach erledigter Arbeit anhält, bis die > nächste 10 ms Marke erreicht ist. Der Haken ist: Immer wenn der Rechner > nichts zu tun hat, legt er sich bis zu 10 ms schlafen. Mäßige Begeisterung. Irgendwie vereinigt diese Technik die Nachteile von Multitasking und Hauptschleife. Das sieht so aus, als ob der Programmierer Multitasking bevorzugt oder braucht, aber sich emotional noch nicht von der Hauptschleife trennen kann. Gibt es einen technischen Grund, warum man das verwendet? Ich mein, außerhalb vom Obfuscated C Code Contest?
Bauform B. schrieb: > Das sieht so aus, als ob der Programmierer Multitasking bevorzugt oder > braucht, aber sich emotional noch nicht von der Hauptschleife trennen kann. RTOS enthält auch eine Hauptschleife, die abwechselnd die Tasks ausführt. Alle Tasks, die nicht event basiert sind, werden regelmäßig aufgerufen, egal ob sie gerade Rechenzeit benötigen oder nicht. > Gibt es einen technischen Grund, warum man das verwendet? Präemptives Multitasking führt häufiger zu unerwarteten Seiteneffekten (race conditions), als Kooperatives. Klar, das sind dann Programmierfehler. Außerdem wird beim Präemptiven Multitasking der Stack und die CPU Register aller unterbrochenen Threads im RAM gesichert, was entsprechend viel RAM erfordert. Wenn man den hat und der dafür nötige Zeitaufwand nicht stört - kein Problem. Bei event basierten Systemen, wo alle Events von Interrupthandlern kommen, bietet sich bei ARM Controllern eine main-loop mit WFI Befehl an, welcher ihn bis zum nächsten Interrupt schlafen legt.
Steve van de Grens schrieb: > RTOS enthält auch eine Hauptschleife, die abwechselnd die Tasks > ausführt. Alle Tasks, die nicht event basiert sind, werden regelmäßig > aufgerufen, egal ob sie gerade Rechenzeit benötigen oder nicht. naja, ja, meinetwegen, das braucht man gelegentlich auch. Würde eine solche Tasks ein sleep() benutzen, wäre sie aber wieder event basiert, jetzt rein von der Terminologie her? > Bei event basierten Systemen, wo alle Events von Interrupthandlern > kommen, bietet sich bei ARM Controllern eine main-loop mit WFI Befehl > an, welcher ihn bis zum nächsten Interrupt schlafen legt. OK, das ist ein guter Kompromiss. Man muss halt auf Schreibschutz verzichten und kann die Tasks nicht einzeln übersetzen und flashen. Ich wollte die soweit wie möglich isolieren, aber dann wird der Datenaustausch umständlich.
Bauform B. schrieb: > Würde eine > solche Tasks ein sleep() benutzen, wäre sie aber wieder event basiert, > jetzt rein von der Terminologie her? Nein, weil sleep() eine (normalerweise) Warteschleife enthält, die so lange wiederholt wird, bis der gewünschte Zeitpunkt erreicht wurde. Bei einem Event basiertem System würde man einen Wecker einstellen, der den task erst dann aufruft, wenn die Zeit gekommen ist. Also ohne Warteschleife. Der Wecker wäre wieder Interrupt-basiert, von der RTC oder einem Timer angetrieben.
Bauform B. schrieb: > Gibt es einen technischen > Grund, warum man das verwendet? Denn der Grund ist doch mehr als offensichtlich: RAM Bedarf Was dann auch genau der Grund dafür ist, dass man die Protothreads nicht mit weiteren Haaren und Federn versehen will, wie z.B. dein Schlafgedönse. Du brauchst es offensichtlich nicht! Außer trollen nix gewesen... Keine Ahnung, aber nörgeln.
Ich kann Bauform schon verstehen. Wiederholt alle Tasks aufzurufen, nur um festzustellen, dass sie nichts zu tun haben, sieht erstmal nach einer gewaltigen Vergeudung von CPU Leistung aus - insbesondere wenn es viele Tasks sind. Aber ein Präemptives Multitasking hat andererseits ebenfalls einen gehörigen Aufwand zu treiben, die Tasks an beliebigen Stellen zu pausieren, um sie später fort zu setzen. Und bei jedem Task, der in einer Warteschleife steht, kann das OS nicht wissen, wann der Task denn demnächst optimal wieder an der Reihe ist. Also finden unnötig viele Taskwechsel statt. Und bei jedem Wechsel werden vielleicht 100 Bytes (oder mehr) hin und her kopiert. Das ist unterm Strich nicht billiger. Es sei denn, die Tasks warten niemals, sondern werden ausschließlich von Events getriggert die wiederum von Hardware Interrupts stammen. Dann kann die CPU sich die meiste Zeit wirklich schlafen legen. Das Prinzip lässt sich jedoch auch ohne RTOS leicht implementieren, wenn denn die Anwendung für diese Art der Ablaufsteuerung geeignet ist. Ich glaube Windows 3.1 Programme funktionierten so.
Steve van de Grens schrieb: > Ich kann Bauform schon verstehen. Wiederholt alle Tasks aufzurufen, nur > um festzustellen, dass sie nichts zu tun haben, sieht erstmal nach einer > gewaltigen Vergeudung von CPU Leistung aus Man muss auch mit RTOS nicht streng zyklisch arbeiten, dafür gibt es ja z.B. Semaphore und Eventflags. Da kann der Scheduler prüfen ob ein Taskwechsel nötig ist.
Ein einfaches Round-Robin-Scheduling hat seine Vorteile. Wenn keiner was zu tun hat, wird durch das Polling zwar etwas Zeit verpulvert, aber das ist nur Zeit, die sonst der Idle-Thread bekommen würde. Wenn allerdings alle Threads was zu tun haben, wird es sehr performant und fair. Ich hatte mal vor ner Ewigkeit einen HTTP-Proxy mit setjmp/longjmp basierenden (kooperativen) Threads[1] gemacht - das Teil war ne Rakete. Insb wenn die Rechnerauslastung auf 100% ging, ging der Overhead des Multitaskings gegen 0. -- [1] Ein Taskwechsel sah so aus:
1 | /* * Leave this thread and give all others a chance to run before coming back. */
|
2 | void
|
3 | thread_yield(void) |
4 | {
|
5 | if (_setjmp(thread_current->env) == 0) |
6 | {
|
7 | thread_current = thread_current->next; /* simple round-robin */ |
8 | _longjmp(thread_current->env, 1); |
9 | }
|
10 | }
|
foobar schrieb: > Ein Taskwechsel sah so aus Ist diese Methode nicht riskant, was den Stack bedarf angeht? Der kann so beliebig anwachsen, oder irre ich mich?
Steve schrieb: > Ist diese Methode nicht riskant, was den Stack bedarf angeht? Der kann > so beliebig anwachsen, oder irre ich mich? Die jmp_bufs referenzieren unterschiedliche Stacks - jeder Thread hat einen eigenen (statischen) Stack. Und ja, die Initialisierung des jmp_bufs ist systemabhängig, jedes C-Library braucht eine spezifische Implementation. Sind zwar nur 2, 3 Zeilen Kode, aber häßlich - ich wünschte, das wäre im C-Standard drin ...
Jo schrieb: > derzeit bin ich dabei einen Task Manager für meine Mikrocontroller > Applikation zu entwickeln. Was die Implementierung betrifft habe ich > noch nicht so ganz den durchblick wie ich swas realisieren könnte. So, du willst also etwas programmieren, wovon du noch keinerlei Ahnung hast? Ein kühnes Ansinnen. Jo schrieb: > Sobald zum Beispiel ein gültiges UART Telegramm empfangen wird, > soll in eine Queue ein Task angemeldet werden. Du neigst dazu, das Pferd vom Schwanze her aufzuzäumen, also mit Sicherheit den falschesten Weg zu wählen. Sauberer und auch besser überschaubar wäre, es etwa so zu machen: 1. Es gibt einen Lowlevel-Treiber, de sich um das (interruptgesteuerte) Empfangen vom UART kümmert und die empfangenen Zeichen in einen Ringpuffer schreibt. 2. Es gibt eine Grundschleife in main(), von wo aus das Kommandoprogramm zyklisch aufgerufen wird. Wenn die Grundschleife nicht allzu weitschweifig daherkommt und deine Plattform nicht gerade mit 32 kHz oder noch weniger getaktet wird, dann wäre so etwa eine Zykluszeit im unteren µs Bereich zu erwarten. 3. Das Kommandoprogramm schaut nach, ob es im o.g. Ringpuffer ein empfangenes Zeichen findet. Wenn nicht, dann sofortiger Rücksprung. Andernfalls wird das Zeichen aus dem Puffer geholt und damit eine Kommandozeile aufgebaut. Mit Längenbegrenzung, damit sie nicht überläuft. Bei einem Zeilenende (CR z.B.) wird die Kommandozeile der auswertenden Funktion übergeben, die dann in der Zeile auf dein 'Telegramm' oder so testet und die zugehörige Aktion veranlaßt. Und für Ereignisse, die nicht aus irgend einer Meldung über einen Empfangskanal kommen, baut man sich einen Ringpuffer für 'Events' bzw. Botschaften. Das übrige Gehabe ist fast gleich, also Test in der Grundschleife, ob so ein 'Event' im Puffer ist und wenn ja, dann wird er dort herausgeholt und ausgewertet. Das, was du zuvor noch lernen mußt, ist nicht blockierend zu programmieren. Also sich nicht mit irgendwas lange aufhalten, weil man sonst eben den ganzen Controller blockiert. Nochwas: Wenn man sich ein anderes Protokoll für die serielle Übertragung ausdenkt, dann kann man diesen Teil des Ganzen eben auch anders machen als über eine Kommandozeile. Aber das heißt: SELBER DENKEN. W.S.
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.