Forum: Mikrocontroller und Digitale Elektronik Task Manager


von Jo (Gast)


Lesenswert?

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?

von Monk (roehrmond)


Lesenswert?

Ich würde mal die Doku von FreeRTOS anschauen. Das ist vermutlich 
vernünftiger, als das Rad neu zu erfinden.

: Bearbeitet durch User
von Jo (Gast)


Lesenswert?

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?

von Monk (roehrmond)


Lesenswert?

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

von Jo (Gast)


Lesenswert?

Danke Steve für den Link.

Jetzt stellt sich die Frage welches Modell für mich geeignet wäre?

von Monk (roehrmond)


Lesenswert?

Jo schrieb:
> Jetzt stellt sich die Frage welches Modell für mich geeignet wäre?

Was meinst du mit "welches Modell"?

von Jo (Gast)


Lesenswert?

Ein Zustandsautomaten oder mehrere Zustandsautomaten.

von Monk (roehrmond)


Lesenswert?

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.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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.

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

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.

von Sebastian (Gast)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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
von Jo (Gast)


Lesenswert?

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.

von Rene K. (xdraconix)


Lesenswert?

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.

von -gb- (Gast)


Lesenswert?

Das was du als Taskmanager bezeichnest ist wohl eher ein 
https://de.m.wikipedia.org/wiki/Prozess-Scheduler

von EAF (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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?

von Harry L. (mysth)


Lesenswert?

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.

von Monk (roehrmond)


Lesenswert?

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.

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

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.
von Michael B. (laberkopp)


Lesenswert?

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
von EAF (Gast)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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 ;-)

von EAF (Gast)


Lesenswert?

foobar schrieb:
> Nein, genau das machen sie.

No!

Mein Protothread Abkömmling nutzt kein switch/case im verborgenen!

von A. S. (Gast)


Lesenswert?

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.

von Monk (roehrmond)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von EAF (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Monk (roehrmond)


Lesenswert?

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!

von Joachim B. (jar)


Lesenswert?

Joachim B. schrieb:
> streng geheim der verwendete µC?

er antwortet einfach nicht, also wieder ein Trollthread.

von Monk (roehrmond)


Lesenswert?

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.

von EAF (Gast)


Lesenswert?

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.

von Bauform B. (bauformb)


Lesenswert?

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
von EAF (Gast)


Lesenswert?

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.

von Monk (roehrmond)


Lesenswert?

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.

von Bauform B. (bauformb)


Lesenswert?

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?

von Monk (roehrmond)


Lesenswert?

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.

: Bearbeitet durch User
von Bauform B. (bauformb)


Lesenswert?

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.

von Monk (roehrmond)


Lesenswert?

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.

von EAF (Gast)


Lesenswert?

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.

von Monk (roehrmond)


Lesenswert?

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.

von J. S. (jojos)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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
}

von Monk (roehrmond)


Lesenswert?

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?

von foobar (Gast)


Lesenswert?

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 ...

von W.S. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.