Hallo,
ich und ein paar meiner Kommilitonene haben ein kleines
Definitionsproblem.
Wir hatten im letzen Semester Realzeitsysteme und es hieß, Preemtion
sei, wenn eine Task unterbrochen werden kann, ohne dass sie fertig mit
ihrer Aufgabe ist.
Nun im neuen Semester, Mikrocontrollertechnik, nutzen wir das RTOS
RTXtiny 166 von Keil. Die Tasks können vom BS unterbrochen werden,
obwohl sie noch nicht fertig sind, was laut "unserer" Definition
bedeutet es sei preemptive. Doch unser Dozent und Keil sagen es sei
non-preemptive weil es nicht prioritätsgesteuert ist.
Was ist denn nun preemption?
Vielen Dank
> Was ist denn nun preemption?
Unterbrechung eines laufenden Tasks weil "seine Zeit abgelaufen ist",
also per Interrupt.
Es spielt keine Rolle, welcher Task nach der preemption als nächster
dran ist, es kann auch derselbe sein, obwohl ohne unterschiedliche
Prioritäten meist round robin passiert, also "der nächste (alle sind
gleich priorisiert also gleichwertig)" drankommt.
Non-preemptive heisst: Der Task gibt (per Aufruf einer Funktion) nur an
kontrollierten Stellen die Kontrolle an das Betriebssystem zurück. Das
vereinfacht die Programmierung, weil man weiß, daß bestimmte
Codeabschnitte (eben die zwischen den Aufrufen) nicht unterbrochen
werden können, also man kein locking braucht.
Das alte Windows 16 war non-preemtive und hat sehr gut funktioniert, die
Benutzertasks reagierten sauber, wer wollte konnte interruptgesteuerte
Treiber bauen die zwar den Benutzertask unterbrachen, aber ihn nie
aufriefen, also kein Problem waren. Win32 wurde preeemtive. Seit dem
hakt es an jeder Front, Kommandos werden verschluckt, gewisse Tasks
blockieren das System bis zum Stillstand und am fatalsten: Wenn man 2
Jobs gleichzeitig startet (z.B. Verzeichnisbauk kopieren unn Virenscan)
dauert es LÄNGER als wenn man sie strikt nacheinander ausführt, es ist
also genau das Gegenteil eines Multitasking-Systems.
Preemtive ist kein Segen, sondern erfordert mehr Verstand und Übersicht,
und wenn man die nicht hat, kommt so was wie bei Microsoft raus.
Non-preemtive zeigt unmittelbar wenn man als Programmierer ein yield
vergessen hat und ist i.A. einfacher zu realisieren. Natürlich hängt bei
non-preemtive das ganze System wenn man eine Endlosschleife ohne yiild
programmiert hat oder sie sich durch Fehler ergeben hat. Ein
"Ctrl-Alt-Del" der dann preemtive eingreift ist also nützlich.
Hallo,
das ging ja schnell,
genau das entspricht ja der ersten Definition. Dann hat Keil keinen Plan
oder was? Die Wikilinks hatte ich auch gefunden, aber das bestätigt auch
nur Def 1 und nicht das was Keil + Dozent meinen.
Danke
Gruß
MaWins Erklärung ist völlig korrekt, wobei man gern den Begriff
"cooperative multitasking" statt "non-preemptive" verwendet. Schon weil
sich daraus direkt das zugrunde liegende Verfahren ergibt: Die Task
kooperiert explizit mit dem BS. Ob Prioritäten im Spiel sind oder nicht
ist unerheblich - wobei ich allerdings bisher noch keinen RT-Kernel
erblickt habe, in dem das nicht vorgesehen wäre.
Schmeiss mal einen Link zu den merkwürdigen Aussagen von Keil rein.
Verwirrter schrieb:> Die Tasks können vom BS unterbrochen werden,> obwohl sie noch nicht fertig sind, was laut "unserer" Definition> bedeutet es sei preemptive.
So stimmts aber dann doch nicht, denn auch in kooperativem Multitasking
können Tasts vor Beendigung unterbrochen werden. Aber eben nur an
Stellen, wo sie das explizit vorsehen.
So gibt es bei solchen Kernels typischerweise eigens einen Kernelaufruf,
der nichts anderes tut, als dem Kernel zu signaliseren, dass jetzt ggf.
eine andere Task ran darf und die laufende Task wieder in die
Warteschlange gestellt werden kann.
Programme für kooperativem Multitasking können daher recht oft mitten in
der Verarbeitung etliche solcher Aufrufe enthalten, um die Wartezeit
anderer kritischerer Tasks gering zu halten. Denn die Reaktionszeit
kritischer Tasks ist eben diese Zeit zwischen solchen Aufrufen (wozu
auch viele andere Kernelaufrufe gehören können).
Es liegt aber in der Natur der Sache, dass preemptives Multitasking
stets mit mit Prioritäten einhergeht, während einfachere kooperative
Kernels auch ohne Prioritäten Sinn ergeben können.
> RTOS RTXtiny 166 von Keil. Die Tasks können vom BS unterbrochen werden,> obwohl sie noch nicht fertig sind
Wie geht es nach der Unterbrechung durch das BS weiter? Kann ein Context
Switch erfolgen oder wird immer zu dem unterbrochenen Task
zurückgekehrt?
Laut Specs-Übersicht
http://www.phaedsys.org/principals/keil/keil-RTOS/keilrtx166.html ist
der preemptive Context Switch beim RTXtiny 166 nicht implementiert.
Helfer schrieb:> Laut Specs-Übersicht> http://www.phaedsys.org/principals/keil/keil-RTOS/keilrtx166.html ist> der preemptive Context Switch beim RTXtiny 166 nicht implementiert.
Diese Übersicht macht klar, dass RTX166-Tiny ein einfaches kooperatives
Multitasking ohne Prioritäten implementiert. D.h. die Task werden
vermutlich zyklisch reihum aktiviert und reihen sich an entsprechenden
Programmstellen freiwillig wieder hinten ein.
> Wie geht es nach der Unterbrechung durch das BS weiter? Kann ein Context> Switch erfolgen oder wird immer zu dem unterbrochenen Task> zurückgekehrt?
Die "Unterbrechung" sieht beispielsweise so aus:
1
while(1){
2
...ArbeitfürzigbishunderteMikrosekunden...
3
RtxYield();
4
}
Der (von mir erfundene) Kernelaufruf RtxYield erlaubt es dem Kernel, an
dieser Stelle auf eine anderes Task umzuschalten. Die laufende Task
landet dann am Ende der Wartschlange und kommt irgendwann später wieder
dran.
@prx
http://www.keil.com/rtx166tiny/
dritter Punkt : Preemptive task switching and task priorities are not
supported
>So stimmts aber dann doch nicht, denn auch in kooperativem Multitasking>können Tasts vor Beendigung unterbrochen werden. Aber eben nur an>Stellen, wo sie das explizit vorsehen.
Ja, das passt mit der Definition überein, habs nur nicht komplett
erklären können.
@helfer
>Wie geht es nach der Unterbrechung durch das BS weiter? Kann ein Context>Switch erfolgen oder wird immer zu dem unterbrochenen Task>zurückgekehrt?
es kann, aber muss nicht, ein Context Switch erfolgen.
gruß
Verwirrter schrieb:> @prx> http://www.keil.com/rtx166tiny/> dritter Punkt : Preemptive task switching and task priorities are not> supported> Die Tasks können vom BS unterbrochen werden,> obwohl sie noch nicht fertig sind
Das deckt sich aber nicht mit dem was ich dort lese.
Da steht explizit, dass nur cooperatives Multitasking unterstützt wird.
Was ja soviel heißt wie: Das Betriebssystem unterbricht von sich aus
keinen Task, sondern der Task muss explizit an das Betriebssystem
zurückgeben und so einen Taskwechsel erlauben.
Aufpassen: Ein Task muss ja nicht 'fertig' sein, wenn er an das BS
zurückgibt. Er kann ja auch einfach nur 'fair' sein und zb den
Wagenrücklauf eines Druckers dazu benutzen, während des Ausdruckens
einem anderen Task eine Chance zu geben. Der Task erlaubt daher, dass er
unterbrochen wird, obwohl er noch nicht fertig ist. Das ist aber etwas
anderes als wie wenn er gnadenlos vom BS nach Ablauf seiner Zeitscheibe
interrupted wird.
Verwirrter schrieb:> Doch, der Task wird vom Betriebssystem unterbrochen, ohne dass er seine> Rechenzeit freiwillig abgibt.> *Getestet mit Keil µVision 3*
Das steht nun aber für RTX 166-Tiny eindeutig im Widerspruch zu Keils
Doku. Würde es entgegen eindeutiger Dokumentation Tasks an beliebiger
Stelle durch andere Tasks (nicht Interrupts!) unterbrechten, dann wäre
es unbenutzbar.
> Doch, der Task wird vom Betriebssystem unterbrochen,> ohne dass er seine Rechenzeit freiwillig abgibt
WOFÜR unterbrochen ?
Um einen Interrupt z.B. der Tatstuar zu behandeln,
die die neue Taste in einen Buffer schreibt, und dann
kehrt das Programm ZU DIESEM Task wieder zurück ?
Das ist in Ordnung.
Das sind verschiedene Ebenen.
Interrupts sind immer erlaubt.
Nur darf er halt nicht an andere Tasks auf derselben
Ebene zurückkehren, mit der Gefahr, an die Stelle,
an der er unterbrochen wurde, ein zweites mal zu
gelangen, ohne daß der vorherige fortgesetzt wurde
und dort "wieder verschwunden" ist und abgeräumt hat.
ist nur Pseudocode unter der Annahme, dass task 0 und task 1 beim OS
angemeldet sind.
Dann sieht man mit dem Logik Analyzer, wie p1 5ms lang toggelt, und dann
p2 5ms lang, dann wieder p1 usw. usf.
Hoffe jetzt ist es etwas klarer.
Gruß
Ok, damit ist klar, dass der non-preemptive Kernel von Keil tatsächlich
preemptive ist. Denn was hier passiert und in der Doku auch aufgeführt
wird ist eben eine preemption, eine Unterbrechung der Task an von der
Task nicht kontrollierbarer Stelle.
Für mich wäre ein solcher Kernel ziemlich problematisch, weil man
deshalb wie in einem preemptive Kernel programmierung muss, ohne aber
über die dafür nötigen Synchronisationsmechanismen wie Semaphoren und
Mutexe zu verfügen. Dieser Kernel kombiniert die Nachteile eines
cooperative Kernels mit den Nachteilen eines preemptive Kernels.
In einem echten non-preemptive Kernel kann sich darauf verlassen, dass
zwischen zwei Kernel-Aufrufen mit Sicherheit nicht auf eine andere
Task umgeschaltet wird. Weshalb man gemeinsam benutzte Daten innerhalb
solcher Codesequenzen nicht durch Mutexe absichern muss und gemeinsam
benutzte Funktionen nicht reentrant sein müssen, was die Programmierung
erheblich vereinfacht und weit weniger fehlerträchtig ist.
Beim Keil Kernel muss man das hingegen genau das tun, hat aber keine
Mutexe zur Verfügung und kann sich daher nur über Abschaltung von
Interrupts oder Zeitkalkulation (keine 5ms seit dem letzten Aufruf von
os_wait) absichern. Das Risiko ist, dass man sich über kurz oder lang
einen extrem schwer nachvollziehbaren Fehler durch Taskunterbrechung an
ungünstiger Stelle einhandelt.
// bewusst als 3 getrennte Operationen formuliert:
4
inttemp=port;// (1)
5
temp^=1<<pin;// (2)
6
port=temp;// (3)
7
}
8
9
voidtoggle_p1(void)task0
10
{
11
while(1)
12
{
13
Toggle(p1);
14
}
15
}
16
17
voidtoggle_p2(void)task1
18
{
19
while(1)
20
{
21
Toggle(p2);
22
}
23
}
Hier kann es nun passieren, dass Task 0 zwischen (1) und (3)
unterbrochen wird und Task 1 zum Zuge kommt. Erstens muss Toggle nun
reentant sein. Zweitens wird Task 0, wenn sie später wieder dran kommt,
den alten vor vielen Millisekunden nach temp geladenen Wert in den Port
schreiben und damit u.U. eine Änderung des zur anderen Task gehörenden
Pins rückgängig machen.
Dieser Code müsste also modifiziert werden, damit er unter allen
Bedingungen funktioniert:
1
voidToggle(intpon)
2
{
3
disable_interrupts();
4
// bewusst als 3 getrennte Operationen formuliert:
A. K. schrieb:> In einem echten non-preemptive Kernel kann sich darauf verlassen, dass> zwischen zwei Kernel-Aufrufen mit Sicherheit nicht auf eine andere> Task umgeschaltet wird.
Round Robin im RTX166 Tiny kann abgeschaltet werden (Round-Robin Timeout
im Konfigurationsfile auf 0 setzen), dann dürfte nicht mehr an nicht
kontrollierbarer Stelle auf eine andere Task umgeschaltet werden.