Hallo Leute,
ich steige direkt ins problem ein:
Ich war gestern auf der Uni bei einer Vorlesung über "Betriebssysteme"
(Sehr interessant). Dort lernte ich die genaue Funktionsweise eines
Multitaskenden Systems kennen. Im Prinzip funktioniert es Ja so.
Es gibt eine Programmtabelle wo alle Daten über die Prozesse gespeichert
sind und jeder Prozess erhält eine Id
Irgendwann gibt es einen interrupt und der dispatcher setzt ein
Alle Werte werden gesichert, das neue Programm geladen und der
Befehlszähler auf Startadresse + eigenen Befehlzähler gesetzt
Soweit so gut, ich würde nun gerne eine art Minimultitaskingsyste für
den Arduino schreiben, aber könnte mir bitte jemand erklären wie ich den
Befehlszähler des Atmega8 in der Arduino IDE manipulieren kann?
Ich danke schon jetzt für die Antworten,
Lg
andreaspaul schrieb:> Soweit so gut, ich würde nun gerne eine art Minimultitaskingsyste für> den Arduino schreiben, aber könnte mir bitte jemand erklären wie ich den> Befehlszähler des Atmega8 in der Arduino IDE manipulieren kann?
Neuen Wert auf den Stack schieben und RET wäre eine Möglichkeit
Hallo,
Was du mit "Befehlszähler" bei einem AVR meinen könntest, ist der
Stackbereich des AVR µP.
Einen Atmega8 würde ich nicht nehmen.
Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM)
u.a. Hier ist RAM dein Freund!
Man kann effizient für jeden Prozess einen eigenen Stackbereich
deklarieren und diesen, neben einigen weiteren Prozess Variable
speichern.
Dann wird es auf einmal einfach - vereinfacht gesprochen - der
Dispatcher schaltet auf den neuen Thread, indem er den alten
(bereinigten) Stack Pointer sichert, und auf den nächsten "umschaltet".
Dann benötigt man auch noch Verfahren, um zwischen den Prozessen Daten
austauschen zu können. Einen direkte Zugriff auf das SRAM (Variable) für
mehrere Prozesse darf es nicht geben.
Karl M. schrieb:> Was du mit "Befehlszähler" bei einem AVR meinen könntest, ist der> Stackbereich des AVR µP.
Gewöhnlich wird mit "Befehlszähler" der Program Counter bezeichnet. Mit
dem Stack hat der, außer dass sein Wert dort abgelegt oder von der
geholt werden kann, nichts weiter zu tun. Der Stack ist ein
Speicherbereich, der als FILO betrieben wird.
andreaspaul schrieb:> ich würde nun gerne eine art Minimultitaskingsyste für den Arduino schreiben,
Das sehe ich als groben Unfug an. Ein Mikrocontroller hat feste
Aufgaben, man ruft aus der "loop()" heraus alle Aufgaben der Reihe nach
auf und lässt diese ausführen.
Manfred schrieb:> Ein Mikrocontroller hat feste Aufgaben, man ruft aus der "loop()"> heraus alle Aufgaben der Reihe nach auf und lässt diese ausführen.
Und was spricht dagegen, den Task Scheduler als einzige Aufgabe zu
betrachten, der dann nach gewünschten Kriterien (Zeitscheibe, Priorität
oder was auch immer) die Prozesse laufen läst.
"alle Aufgaben der Reihe nach" ist nur eine Möglichkeit davon.
Ok danke für die Antworten liebe Leute, alle sehr Hilfreich ;)
Ich schau mir mal die Links an und das mit der Stack ist interessant.
Mit welchem Arduino IDE behelfen kann man auf diese zugreifen bzw wie
kann man in der Arduino IDE prinzipiell den RAM einteilen, verwalten
etc.
(Und noch eine Sache an dich Manfred, wenn du das hier als "Unfug"
ansiehst und nichts Produktives dazu sagst -> Halt dich raus!
Ich habe nicht nach deiner Meinung sondern nach einer produktiven Lösung
gefragt.
Kleingeister die alles als Blödsinn und Unfug abstempeln und über nichts
nachdenken wollen hab ich im Alltag genug. Mir geht es hier um das
Lernen, dass man mit sowas kein Softwarebaron wird weiß ich auch. Mein
Tipp an dich: Denk über dinge nach, lass dich begeistern und Stempel
nicht alles als "Unfug ab" "Think different! (Zitat Apple Inc.)"
Ich hoffe ich hab jetzt euch anderen nicht mit dieser kurzen "rede"
vergrault und hofft dass ich meine Meinung über Manfreds Post versteht.)
An die anderen einen großen Dank für eure Antworten Ihr seid alle super,
Danke und alles Liebe,
Paul
>Einen Atmega8 würde ich nicht nehmen.
Ich auch nicht.
>Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM)
Auch den würde ich nicht nehmen. Eher einen ARM Cortex M3 oder M4
und dazu FreeRtos. Dann muss man nichts mehr selber machen. Gibt es
alles schon und funktioniert super.
andreaspaul schrieb:> aber könnte mir bitte jemand erklären wie ich den> Befehlszähler des Atmega8 in der Arduino IDE manipulieren kann?
Der "Befehlszähler" ist doch der Program Counter. Allerdings ist das
eher ein Programmzeiger.
Kenne den Befehlssatz dieser MPU nicht, aber Manipulieren kann man
Programmzeiger einfach, z.B. call oder jp. Auch ret "manipuliert" ihn.
Aber das war eine Scherzfrage, oder ;)
andreaspaul schrieb:> (Und noch eine Sache an dich Manfred, wenn du das hier als "Unfug"> ansiehst und nichts Produktives dazu sagst -> Halt dich raus!
Ich finde deine Reaktion zu hart.
Du willst ein RTOS selber bauen, ohne eines zu kennen (um dessen
Strategien erforschen zu können), ok. Sportlich, und Du lernst sehr
viel.
So solltest dabei aber nicht vergessen, dass es auch den SPS-Loop-Ansatz
gibt, dass dieser in vielen Fällen deutlich robuster, einfacher und
deterministischer ist, und dass er für Anfänger noch steilere Lernkurven
hat.
Achim S. schrieb:> So solltest dabei aber nicht vergessen, dass es auch> den SPS-Loop-Ansatz gibt, [...]
Sicher. Die Ironie dabei ist, dass industrielle SPS
durchaus eine Art Betriebssystem haben, denn das
Speicherabbild der Eingänge bzw. Ausgänge stellt sich
nicht von allein her, und auch die Hintergrund-
kommunikation zu irgendwelchen Terminals bzw. zum
Programmiergerät muss jemand machen.
Der Gegensatz ist also bei weitem nicht so scharf,
wie er hier konstruiert wird.
holger schrieb:> Auch den würde ich nicht nehmen. Eher einen ARM Cortex M3 oder M4> und dazu FreeRtos. Dann muss man nichts mehr selber machen. Gibt es> alles schon und funktioniert super.
Nur das Lernen funktioniert damit nicht. Wenn alles vorgekaut da liegt
und funktioniert, besteht meist kein Anlass, sich mit den Detais zu
beschäftigen und etwas ggf. auch noch mal selber zu erfinden.
andreaspaul schrieb:> Mit welchem Arduino IDE behelfen kann man auf diese zugreifen bzw wie> kann man in der Arduino IDE prinzipiell den RAM einteilen, verwalten> etc.
Da wirst du dich wohl auf AVR-Assembler Ebene herunter bewegen müssen
und dann mit in den C-Code einbinden.
Um den "Befehlszähler" zu manipulieren, gibt es in der C-Library einen
Standardmechanismus (der zwar nicht unbedingt schön ist, aber portabel
und das Assemblergefummel spart). Der heißt longjmp()/setjmp().
Und der berücksichtigt auch gleich die Tatsache, daß es mit einer
Program Counter Manipulation ja nicht getan ist, wenn man den CPU
Kontext wechseln will. Da gehören ja noch (mindestens) alle
Registerinhalte dazu.
holger schrieb:>>Einen Atmega8 würde ich nicht nehmen.>> Ich auch nicht.>>>Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM)>> Auch den würde ich nicht nehmen. Eher einen ARM Cortex M3 oder M4> und dazu FreeRtos. Dann muss man nichts mehr selber machen. Gibt es> alles schon und funktioniert super.
Wenn die Intention ist, ein RTOS selber zu schreiben, dann ist das ein
schlechter Rat. Wenn Linus Torvalds auf einen solchen Rat gehört hätte
(und tatsächlich hat er ihn von seinem Prof bekommen: "nimm Minix") dann
gäbe es heute kein Linux.
Daß der TE allerdings weit davon entfernt ist, das zu können, steht auf
einem anderen Blatt. Ebenso, daß das Arduino Framework (nicht: die
Arduino Hardware-Plattform) dafür nun wirklich ungeeignet ist.
Danke liebe Leute, ich werde mich mal mit longjmp() und den Rios
vertraut machen. Danke für die guten antworten ;)
Ihr seid alle toll,
Alles Liebe Paul
Egon D. schrieb:> Der Gegensatz ist also bei weitem nicht so scharf,> wie er hier konstruiert wird.
Nein, scharf sowieso nicht. Zumal nicht jede Implementierung alle
Features hat:
a) Interrupts (mit verschiedenen Prioritäten), ausgelöst durch z.B.
Timer sind prinzipiell auch ein RTOS (ohne Sleep).
b) Für ein Autosar-RTOS sind Basic-Tasks empfohlen. Das sind quasi nur
SPS-Funktionen, auch ohne Sleep. Zwar mit verschiedenen Prioritäten,
aber jede "Task" soll nach jedem Aufruf zurückkehren.
c) Ja, auch bei SPS-Loop-Systemen sollte man z.B. für die GUI eine
eigene Tasks haben.
d) Die Windows-Nachrichtenloop ist wieder eine andere Metapher.
Asynchrone Ereignisse mit quasi synchroner Verarbeitung.
Und dann noch beim "normalen" RTOS Prios und Round-Robin, preemptiv und
kooperierend, ... die Reine Lehre gibt's (zum Glück) nirgends...
Ok danke.
Leider komme ich mit longjmp() nicht wirklich weiter ;( Wäre jemand so
lieb mir ein Stück kommentierten Code zu geben mit dem man über die
Stack den Wert des Programmcounters verändern kann?
Danke und Alles Liebe, Paul
andreaspaul schrieb:> Leider komme ich mit longjmp() nicht wirklich weiter ;( Wäre jemand so> lieb mir ein Stück kommentierten Code zu geben mit dem man über die> Stack den Wert des Programmcounters verändern kann?
Du bist massiv verwirrt. In C - wo du dich anscheinend aufhältst - gibt
es kein Konzept "Program Counter". Das ist ein Konzept vom
Maschinensprache-Niveau. Deswegen kannst du den Programmzähler von C aus
weder sehen noch direkt verändern. Indirekt natürlich schon. Immer wenn
dein C-Programm zur nächsten Zeile fortschreitet, ändert sich der
Programmzähler. Wenn du eine C-Funktion aufrufst, ändert er sich auch.
usw. usf.
Den Programmzähler kannst du nur sehen, wenn du dich auf die Maschinen-
Ebene begibst. Z.B. indem du den Ablauf deines Programms mit dem
Debugger verfolgst. Dann gibt es ein CPU-Register namens "PC" - das
steht für Program Counter. Es gibt auch einen Maschinensprach- (vulgo:
Assembler-) Befehl, um den PC zu ändern. Der heißt typisch JMP (für
Jump) und nimmt als Argument den neuen Wert für den PC. Z.B. setzt JMP
1234 den PC auf den Wert 1234. Nicht daß dir das jetzt direkt
weiterhelfen würde.
Wenn du diese Interna wirklich verstehen möchtest, dann würde ich dir
empfehlen, ein Assembler-Tutorial zum AVR durchzuarbeiten. Z.B. das von
dieser Seite: AVR-Tutorial
Oder eins der gefühlt 1000 weiteren im Web:
https://www.google.com/search?&q=avr%20assembler%20tutorial
Axel S. schrieb:> Du bist massiv verwirrt. In C - wo du dich anscheinend aufhältst - gibt> es kein Konzept "Program Counter". Das ist ein Konzept vom> Maschinensprache-Niveau. Deswegen kannst du den Programmzähler von C aus> weder sehen noch direkt verändern. Indirekt natürlich schon. Immer wenn> dein C-Programm zur nächsten Zeile fortschreitet, ändert sich der> Programmzähler. Wenn du eine C-Funktion aufrufst, ändert er sich auch.> usw. usf.
Das stimmt so nicht. Jede C-Library, die setjmp()/longjmp() (bzw.
sigsetjmp()/siglongjmp()) korrekt implementiert (und das sollte
eigentlich jede einigermaßen ernstzunehmende tun), erlaubt (mehr oder
weniger direktes) Verändern des Programmzählers. Jedes Mal, wenn Du
STRG-C drückst, wechselt die Task. Ohne eine einzige Zeile Assembler,
selbst ohne zu wissen, welche CPU überhaupt werkelt:
1
#include<stdio.h>
2
#include<setjmp.h>
3
#include<stdbool.h>
4
#include<signal.h>
5
6
jmp_bufcontexts[3];
7
staticvolatilesig_atomic_ttask=1;
8
voidhandler(int);
9
10
voidtask0(void)
11
{
12
if(sigsetjmp(contexts[0],1))
13
{
14
while(true)
15
{
16
printf("Hello from task0 (task=%d)\r",task);
17
}
18
}
19
}
20
21
voidtask1(void)
22
{
23
if(sigsetjmp(contexts[1],1))
24
{
25
while(true)
26
{
27
printf("Hello from task1 (task=%d)\r",task);
28
}
29
}
30
}
31
32
voidtask2(void)
33
{
34
if(sigsetjmp(contexts[2],1))
35
{
36
while(true)
37
{
38
printf("Hello from task2 (task=%d)\r",task);
39
}
40
}
41
}
42
43
voidhandler(intsig)
44
{
45
printf("\nsignal handler task=%d\n",task);
46
task=(task+1)%3;
47
printf("\nswitch to task %d\n",task);
48
longjmp(contexts[task],1);
49
}
50
51
52
intmain(intargc,char*argv[])
53
{
54
signal(SIGINT,handler);
55
/*
56
* init contexts
57
*/
58
task0();
59
task1();
60
task2();
61
62
while(true)
63
{
64
}
65
}
Obwohl das Beispielprogramm so nicht (ganz) clean ist (es macht
stdio-Ausgaben im Signal-Handler, aber anders sähe man nichts), sollte
es 100%ig portabel sein.
Wenn Du aus "SIGINT" "SIGALRM" machst, hast Du einen (rudimentären)
Multithreading-Kernel.
Ohne eine einzige Zeile Assembler.
Markus F. schrieb:> Axel S. schrieb:>> Du bist massiv verwirrt. In C - wo du dich anscheinend aufhältst - gibt>> es kein Konzept "Program Counter". Das ist ein Konzept vom>> Maschinensprache-Niveau. Deswegen kannst du den Programmzähler von C aus>> weder sehen noch direkt verändern. Indirekt natürlich schon. Immer wenn>> dein C-Programm zur nächsten Zeile fortschreitet, ändert sich der>> Programmzähler. Wenn du eine C-Funktion aufrufst, ändert er sich auch.>> usw. usf.>> Das stimmt so nicht. Jede C-Library, die setjmp()/longjmp() (bzw.> sigsetjmp()/siglongjmp()) korrekt implementiert (und das sollte> eigentlich jede einigermaßen ernstzunehmende tun), erlaubt (mehr oder> weniger direktes) Verändern des Programmzählers.
(Beispiel geschnippelt)
Schön. Nur ist das kein bißchen direkter als ein Funktionsaufruf oder
meinetwegen auch goto. Du hast nach wie vor nicht so etwas wie einen
"Befehlszähler", dessen Wert du sehen oder direkt ändern könntest.
Du hast nur mehr oder weniger abstrakte Labels, zu denen du hinspringen
kannst. Du hast weder eine Möglichkeit, einen "Wert" dieser Labels zu
sehen noch hast du Einfluß darauf, wo sie im Speicher abgelegt werden.
Ganz im Gegensatz zu Assembler, wo du per org Direktive festlegen
kannst, wo im Speicher dein Code landet oder wo du jmp die Adresse auch
einfach als Zahl geben kannst.
Für jemanden, der das ganze aus Assemblersicht betrachtet, ist setjmp()
sogar viel komplexer, weil es ganze Stackframes sichert und (bei
longjmp) wiederherstellt.
Axel S. schrieb:> Du hast nur mehr oder weniger abstrakte Labels, zu denen du hinspringen> kannst. Du hast weder eine Möglichkeit, einen "Wert" dieser Labels zu> sehen noch hast du Einfluß darauf, wo sie im Speicher abgelegt werden.> Ganz im Gegensatz zu Assembler, wo du per org Direktive festlegen> kannst, wo im Speicher dein Code landet oder wo du jmp die Adresse auch> einfach als Zahl geben kannst.
Du verkennst setjmp()/longjmp(). Das ist nicht nur ein "einfaches goto",
das ist ein kompletter Kontext-Switch. Und was interessieren mich "Werte
meiner Labels" oder wo die im Speicher liegen?
Vielleicht nochmal zurück zur eigentlichen Ausgangsfrage: der TO wollte
ein Multitasking-System schreiben und erhielt die Auskunft, daß das nur
mit C und ohne Assembler nicht (und mit seinem Wissenstand schon gar
nicht) ginge.
Darum ging's mir. Und daß ich dabei deine Antwort gequotet habe, ist
eigentlich nur Zufall (ich hätte auch fast jede andere nehmen können).
Es geht (wenn auch sehr rudimentär). Und war in weniger als zehn Minuten
runtergeschrieben und lauffähig. Mich würde interessieren, wie lang
jemand braucht, ähnliche Funktionalität in Assembler zum Fliegen zu
bringen.