Hi,
ich hab da eine Verständnissfrage zu atomarem Zugriff auf 16 bit
Variablen.
So wie ich das verstanden habe sollte eine Variable die in einer ISR und
in anderen Funktionen wie z.B. der Hauptschleife verwendet wird als
static volatile deklariert werden und wenn es eine 16Bit Variable ist
dann sollte vor dem Zugriff ausserhalb der ISR die Interrupts
ausgeschlatet und danach wiedser eingeschlatet werden damit der Wert der
Variable während des Zugriffs nicht durch die ISR verändert werden kann.
z.B.:
1
//...
2
staticvolatileuint16_ta;
3
4
voidISR()
5
{
6
a++;
7
}
8
9
intmain()
10
{
11
//...
12
sei();
13
//...
14
while(1)
15
{
16
cli();
17
if(a==...)
18
{
19
//...
20
}
21
sei();
22
...oder:
23
cli();
24
uint16_tb=a;
25
sei();
26
if(b==...)
27
{
28
//...
29
}
30
}
31
//...
32
return0;
33
}
... wenn jetzt aber sagen wir "a" sehr wichtig für die Funktionalität
des Programms ist z.B. für eine synchronisierung eines Protokolls etc.
und ich den ISR auf gar keinen Fall "verpassen" darf, wäre es ja schon
nicht so schlau beim Zugriff in der Hauptschleife die Interrupts
auszuschalten.
Wie geht man bei sowas am Besten vor?
Thx
Grobi
Grobi schrieb:> ich den ISR auf gar keinen Fall "verpassen" darf, wäre es ja schon> nicht so schlau beim Zugriff in der Hauptschleife die Interrupts> auszuschalten.
Den kann man nicht verpassen, der wird ausgeführt sobald sei ausgeführt
wurde. Also wird er, bei korrekter Handhabung, nur um ein paar
Prozessortakte verzögert. Man darf nur zwischen cli und sei nicht
längere Zeit verstreichen lassen, cli - laden - sei hat keinen
merklichen Einfluss auf das Verhalten des Systems.
Georg
Grobi schrieb:> wäre es ja schon> nicht so schlau beim Zugriff in der Hauptschleife die Interrupts> auszuschalten.
Wenn Dein Interrupt nichtmal 4 Zyklen Sperre verkraftet, dann hast Du
die falsche CPU gewählt.
Schon jedes RET sperrt beim AVR die Interrupts für 4 Zyklen.
Grobi schrieb:> den ISR auf gar keinen Fall "verpassen" darf
Die meisten IRQs setzen ein Flag, und das passiert auch bei
deaktivierten Interrupts. Nach einem SEI werden diese IRQs nachgeholt.
Es werden alse keine IRQs verpasst, es sei denn
o Während der Sperre wird die gleiche IRQ mehr als 1x getriggert
o Die IRQ setzt kein Flag, was bei manchen Level-Triggered IRQs
der Fall ist.
Da dein Code bis zur maximalen Unkenntlichkeit verunstaltet ist, lässt
sich nix konkretes darüber sagen.
Ah ok, Danke für die Infos, war mir nicht klar das der Interrupt
sozusagen nachgeholt wird sobald er wieder durch sei freigegeben wird.
Also sollte die Variante :
cli();
uint16_t b = a;
sei();
im Prinzip die "sicherste" in diesem Fall sein.
"Da dein Code bis zur maximalen Unkenntlichkeit verunstaltet ist, lässt
sich nix konkretes darüber sagen." - das war auch nur ein schnell
dahingeschmiertes Beispiel.
Vielen Dank!
BtW:
Grobi schrieb:> wird als> static volatile deklariert
Ein "static" vor einer globalen Variablen ist unnötig/unsinnig.
Globale variablen sind immer "static".
Max M. schrieb:> BtW:>> Grobi schrieb:>> wird als>> static volatile deklariert>> Ein "static" vor einer globalen Variablen ist unnötig/unsinnig.
Ist es nicht. Static bewirkt hier, dass die Variable von außerhalb des
Moduls nicht sichtbar ist. Sie also gerade wegen dem static NICHT global
sichtbar.
> Globale variablen sind immer "static".
So ein Quatsch.
Nein, im globalen Kontext steuert man mit dem Schlüsselwort static, dass
die damit gekennzeichnete Variable/Funktion nur in dieser einen Datei
verwendet werden kann.
Das ist eine der Feinheiten, die ich an C nicht so gerne habe.
Grobi schrieb:> und wenn es eine 16Bit Variable ist> dann sollte vor dem Zugriff ausserhalb der ISR die Interrupts> ausgeschlatet und danach wiedser eingeschlatet werden damit...
Das ist - mal ganz grob gesagt - ein ganz schlechter Stil. Vielleicht
kann man sowas auf den AVR's machen, aber woanders fällt man bei sowas
fast immer auf die Nase. Bei den klassischen ARM-Controllern z.B. läuft
main und alle von dort aufgerufenen Programmteile zumeist im User-Modus,
da gibt es keine Möglichkeit, mal eben die Interrupts zu sperren. Das
geht dort nur im Supervisor-Modus.
Die Lösung für dein eigentliches Problem ist schlichtweg eine
geeignete Synchronisation zwischen ansonsten unkorrelierten
Programmteilen. Wieso sollten denn auch bloß zwei verschiedene
Programmteile SCHREIBEND auf die selbe variable zugreifen? Sowas braucht
man regelmäßig nicht, wenn man sich ein paar Gedanken zuvor gemacht hat.
W.S.
W.S. schrieb:> Bei den klassischen ARM-Controllern z.B. läuft main und alle von dort> aufgerufenen Programmteile zumeist im User-Modus, da gibt es keine> Möglichkeit, mal eben die Interrupts zu sperren.
Quatsch, nur wenn man ein OS verwendet kann das so sein. Im normalen
"bare metal " Betrieb ist es auch beim ARM überhaupt kein Problem, im
Interrupt die Interrupts zu sperren.
W.S. schrieb:> Wieso sollten denn auch bloß zwei verschiedene Programmteile SCHREIBEND> auf die selbe variable zugreifen? Sowas braucht man regelmäßig nicht,> wenn man sich ein paar Gedanken zuvor gemacht hat.
Das Problem mit der Atomizität hat man auch, wenn man nur an einer
Stelle schreibt. Und selbstverständlich muss man oft auch von mehreren
Interrupts schreiben, zB wenn man einen Ringpuffer hat der von
verschiedenen Interrupts gelesen & geschrieben wird - dann muss man die
Zeiger von allen diesen Interrupts aus anpassen.
Im bare metal Betrieb ist die Interruptsperre das gängige Mittel zur
Synchronisation. Die klassischen Thread Synchronisations Mittel wie
Mutexe machen nur Sinn wenn man ein (RT)OS mit Scheduler verwendet, was
bei weitem nicht immer der Fall ist, auch nicht auf dem ARM.
W.S. schrieb:> Wieso sollten denn auch bloß zwei verschiedene> Programmteile SCHREIBEND auf die selbe variable zugreifen?
das Problem tritt doch auch auf wenn nur ein Programmteil schreibt (der
Interrupt) und im anderen gelesen (main) gelesen wird.
main liest dann z.B. das erste Byte des Worts, jetzt kommt der Interrupt
und verändert das Wort und dann holt sich main das zweite Byte und hat
jetzt ein Wort gelesen, das aus zwei verschiedenen Worten
zusammengesetzt ist
Loddaar schrieb:> das Problem tritt doch auch auf wenn nur ein Programmteil schreibt (der> Interrupt) und im anderen gelesen (main) gelesen wird.
Klar, was denn sonst. Hier werden primitivste Grundlagen zerredet, die
seit 40 Jahren vollkommen geklärt und ausdiskutiert sind, und die Folge
cli - Varaible lesen/schreiben - sei ist jedem Programmierer vertraut,
der sein Handwerk versteht.
W.S. schrieb:> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.
Das ist - mal ganz grob gesagt - vollkommene Ahnungslosigkeit.
Georg
Auch als Arduino Jünger weiß ich, dass in solchen Fällen, das sperren
der Interrupts, und sei es auch nur dieser eine kritische, nahezu
alternativlos ist.
Das Eingangsposten "riecht" nach 8Bit AVR.
Also sind die ARM Gedichte sowieso .....
Georg schrieb:> Klar, was denn sonst. Hier werden primitivste Grundlagen zerredet, die> seit 40 Jahren vollkommen geklärt und ausdiskutiert sind, und die Folge> cli - Varaible lesen/schreiben - sei ist jedem Programmierer vertraut,> der sein Handwerk versteht.
wobei man schauen kann ob die CPU dafür spezielle Atomic befehle hat.
Beim x86 z.b. InterlockedIncrement
könnte mir vorstellen, das es auch bei ARM so etwas gibt.
Georg schrieb:>> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.> Das ist - mal ganz grob gesagt - vollkommene Ahnungslosigkeit.
Das ist - mal ganz grob gesagt - eine stillose Keilerei.
Es gibt mehrere Wege. Je nach Umgebung. Und je nachdem, ob man mit
Interrupts oder parallelen Threads zu tun hat. Was bei AVR richtig ist
kann anderswo falsch sein.
Bei AVR, wo man weder User-Mode hat noch irgendwelche Realtime-Kernels
oder gar Betriebssysteme regelmässig mit sich rumträgt, läuft es kaum
vermeidbar auf cli/sei raus, wobei dies bei Verwendung vom GCC
sinnvollerweise hinter ATOMIC_BLOCK(...) versteckt wird.
Andere Prozessoren mit bare metal programming ohne RTOS haben teilweise
andere Methoden dafür. So kann man bei priorisierten Interrupt-Systemen
statt alle Interrupts abzuschalten auch dafür sorgen, dass der
Interrupt-Level zeitweilig grad so weit angehoben wird, dass der
betreffende Interrupt kurz gesperrt ist, aber höher priorisierte
ungebremst durchkommen. Die Cortex M besitzen dafür ein recht speziell
arbeitendes Register BASEPRI_MAX.
Spezielle nicht-atomare Befehle für den Umgang mit solchen Daten hatte
ich vorhin verlinkt. Solche Befehle sind charakteristisch für RISC
Prozessoren, bei denen der komplexe Ablauf von atomaren
read-modify-write Befehlen untypisch für deren Arbeitweise ist. Die
können dann auch in Varianten auftreten, je nachdem ob es dabei um
Interrupts oder um parallele Cores geht.
CISC Prozessoren wie x86 wiederum verfügen oft über diverse Befehle zur
atomaren Manipulation von Daten.
Wenn man ein RTOS oder gar ein komplettes Betriebssystem drauf hat, dann
verwendet man sinnvollerweise dessen Methoden, wobei man bei Spinlocks
wieder ungefähr auf dem Level der ldrex/strex Befehle landet. Im
Usermode wohlgemerkt. Und man in der Oberklasse noch weitere Feinheiten
berücksichtigen darf, aber da sind wir hier wohl eher nicht.
Und wem das alles immer noch zu einfach ist, der schaut mal rein, was
Intel mit Haswell dazu neu eingebracht hat. ;-)
A. K. schrieb:> läuft es kaum> vermeidbar auf cli/sei raus
Und das hast du als schlechten Stil bezeichet. Hast du dich inzwischen
weitergebildet? Pseudointelligentes Geschwafel über Usermodus ist völlig
fehl am Platz, Hardware mit Interruptsteuerung kann man nun mal bloss
bedienen, wenn man auch auf Interrupt Control Bits zugreifen kann und
ISR ausführen kann, also eben nicht im User Modus. Jemanden, der einen
Treiber für interruptgesteuerte Peripherie-Hardware schreibt schon
allein deswegen eines schlechten Stils zu beschuldigen ist einfach eine
Frechheit. Da ein Controller ohne Peripherie nutzlos ist, ist das eine
Notwendigkeit, auch wenn du glaubst dass du über sowas erhaben bist.
Und das ist nicht nur bei AVR so, selbst x86-Prozessoren verwenden
Interrupts. Und die schalten sich nicht von selbst aus und ein.
Georg
W.S. schrieb:> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.
Nein, ist es nicht. Auf AVR8 ist es sogar praktisch (fast) die einzige
Möglichkeit zur Synchronisation zwischen ISRs untereinander und ISRs und
main. Die einzige Alternative daz wäre die spezifische Sperrung von IRQs
mittels der entsprechenden Maskenbits.
> Vielleicht> kann man sowas auf den AVR's machen
Nicht "kann", nein man MUSS es so tun.
> aber woanders fällt man bei sowas> fast immer auf die Nase. Bei den klassischen ARM-Controllern z.B. läuft> main und alle von dort aufgerufenen Programmteile zumeist im User-Modus,> da gibt es keine Möglichkeit, mal eben die Interrupts zu sperren. Das> geht dort nur im Supervisor-Modus.
Da bietet dann aber auch die Hardware andere Mechanismen zur
Synchronisation an.
> Wieso sollten denn auch bloß zwei verschiedene> Programmteile SCHREIBEND auf die selbe variable zugreifen? Sowas braucht> man regelmäßig nicht, wenn man sich ein paar Gedanken zuvor gemacht hat.
Bullshit, das ist gang und gäbe, es wird z.B. regelmäßig für FIFOs
benötigt. Es ist nicht gerade ungewöhnlich, Daten zwischen ISRs und main
per FIFO zu buffern. Und dazu gehört halt zwingend der atomare Zugriff
auf Schreib- und Lesepointer (bzw. -offsets).
Georg schrieb:> Hast du dich inzwischen> weitergebildet? Pseudointelligentes Geschwafel über Usermodus ist völlig> fehl am Platz,
Danke für die Blumen. Hast du damit wirklich mich gemeint? Zu viel
Emotion kann auch mal die Sicht behindern.
Auch das wäre eine Möglichkeit, Norbert. Ich sehe das aber auch wie
manch anderer hier: Bei den AVRs ist das keine Schande mal eben zum
Lesen einer Variablen die Interrupts abzuschalten. Wieviel Interrupts
sollen denn in 4 Zyklen kommen die man verpassen könnte? Wenn man es so
zeitkritisch hat ist vielleicht auch ein AVR nichts für die gestellte
Aufgabe.
Insbesondere, da der AVR keine Interruptlevel hat.
D.h. wenn man noch andere Interrupts benutzt, ist die Sperre durch einen
gerade aktiven Handler mindestens 40 Zyklen. Dagegen sind nur 4 Zyklen
ein Witz.
Sogar ein völlig leerer Handler sperrt sich selber für die nächsten
11..14 Zyklen. Daher sieht man es manchmal für sehr schnelle Sequenzen,
daß kein Interrupt benutzt wird, sondern Polling (3 Zyklen).
Norbert schrieb:> Wäre so etwas nicht auch möglich falls man auf gar keinen Fall> Interrupts abschalten will?
Nun ja, so ähnlich etwa.
Es gibt immer und auf allen Architekturen die Möglichkeit, Daten
zwischen unabhängigen Programmteilen auszutauschen, ohne Interrupst zu
sperren.
Das, was hier der Ausgangspunkt war, ist ja das KONKURRIERENDE Zugreifen
auf die gleiche Speicherstelle.
Aber wozu dies?
Wenn man für sowas einen winzigkleinen Fifo (sprich Ringpuffer)
einführt, ist alles entkoppelt. Der eine Prozeß hat exclusiven Zugriff
auf den Schreibzeiger und der andere auf den Lesezeiger. Fertig.
Eine andere Methode ist das Verwenden von "events", noch eine andere ist
das Benutzen von zwei simplen Aktualisierungscodes (ist ähnlich zum
Fifo). Kurzum, es gibt ne Menge von Verfahren, die völlig ohne solche
Brutalmethoden wie dem Sperren von Interrupts auskommen.
c-hater schrieb:>> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.>> Nein, ist es nicht.
Oh ist es doch. Das Prinzip ist ja immer, daß ein Programmteil einem
anderen Programmteil etwas mitteilen will. Es gibt also immer einen
"Ansager" und einen "Zuhörer" und man muß lediglich darauf achten, daß
diese Art Kommunikation einseitig ist. Wenn man also bidirektional sich
was mitteilen will, muß man zwei Mechanismen (Ansager-->Zuhörer)
einrichten, für jede Richtung eben einen - ganz generell formuliert. Und
wenn es noch mehr Programmteile geben sollte, die irgendwelche
Mitteilungen zur Kenntnis nehmen müssen, dann eben per
"Event-Broadcast".
W.S.
> Der eine Prozeß hat exclusiven Zugriff auf den> Schreibzeiger und der andere auf den Lesezeiger. Fertig.
Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller
diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?
Stefan U. schrieb:>> Der eine Prozeß hat exclusiven Zugriff auf den>> Schreibzeiger und der andere auf den Lesezeiger. Fertig.>> Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller> diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?
In diesem speziellen Fall geht das wirklich, wenn man es richtig macht
und diese Zeiger (ggf. Indexe) mit der maximalen atomaren Wortbreite der
Maschine glücklich sind. Dann ein N-Byte FIFO maximal N-1 Bytes puffern.
Also maximal 255 Bytes bei AVRs.
Allerdings halte ich die Aussage, dass man immer ohne Interrupt-Sperre
auskommt, für reichlich gewagt.
Stefan U. schrieb:> Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller> diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?
Indem der eine Prozeß eben den EXCLUSIVEN Zugriff auf den Schreibzeiger
hat und der andere selbigen auf den Lesezeiger. Lerne Programmieren und
lies nach, wie man einen Fifo bzw. Ringpuffer schreibt. Herrjenochmal!
W.S.
W.S. schrieb:> Indem der eine Prozeß eben den EXCLUSIVEN Zugriff auf den Schreibzeiger> hat und der andere selbigen auf den Lesezeiger.
Das mir bekannte Verfahren benötigt schreibenden Zugriff auf den eigenen
Zeiger und lesenden Zugriff auf den anderen. Exklusiv ist er also nicht.
Daraus ergibt sich aber die eben erwähnte Einschränkung auf atomare
Lese- und Schreibzugriffe.
A. K. schrieb:> Allerdings halte ich die Aussage, dass man immer ohne Interrupt-Sperre> auskommt, für reichlich gewagt.
Nö. es geht prinzipiell immer.
Der Knackpunkt ist ja nichts anderes als die Daten-Korruption, die genau
dadurch zustande kommt, daß es zwei unkorrelierten Prozessen erlaubt
ist, auf die gleiche Ressource zuzugreifen. Das gilt nicht nur für den
Fall des TO mit seinem 16 Bit Wort, sondern auch für den Betrieb von
Schnittstellen aller Art, wo nicht gepollt, sondern auch mit Interrupts
gearbeitet werden soll.
W.S.
A. K. schrieb:> Das mir bekannte Verfahren benötigt schreibenden Zugriff auf den eigenen> Zeiger und lesenden Zugriff auf den anderen. Exklusiv ist er also nicht.
Dann guck in die Lernbetty ;-)
Also: das Programm, was den Fifo füllt, darf den Schreibzeiger verändern
und den Lesezeiger nur lesen (aber nicht verändern). Das Programm, was
den Fifo leert, darf den Lesezeiger verändern, aber auf den
Schreibzeiger nur lesend zugreifen. Fazit: ein jedes Programm hat seine
exclusive Ressource (jeder seinen Zeiger) und keines kommt dem anderen
in die Quere.
Das einzige, was da noch kommen könnte, wäre ein drohendes Überlaufen
des Fifo - aber das kann nur aufgrund eines krepierten Threads oder
sonstigen generellen Fehlers im Systementwurf vorkommen. Der andere Fall
(Fifo ist leer) ist normal, denn in diesem Falle haben sich beide
Programme eben mal nix zu sagen.
W.S.
W.S. schrieb:> Nö. es geht prinzipiell immer.
Dann will ich es mal anders ausdrücken: Hältst du es wirklich für guten
Stil, auf Prozessoren der AVR Kategorie in jedem Fall auf
Interrupt-Sperre zu verzichten? Mich deucht, dass diese Verfahren die
Komplexität der Programme deutlich erhöhen werden, ohne aber in den
meisten Fällen irgendwelche Vorteile zu bringen.
Stelle dabei diesen Lösungen einen Code wie in
ATOMIC_BLOCK(...)
{
...
}
gegenüber. Der das auf recht elegante Art kapselt, technisch aber nichts
anderes ist als disable/enable oder save/disable/restore.
Das lässt sich auch verallgemeinern, wenn du solche AVR Kleinigkeiten
unbedingt als einen Aspekt von Interprozess-Kommunikation ausdrücken
willst. Vom Rendevouz-Konzept im ursprünglichen Ada abgesehen bin ich
noch keinem API zur Interprozess-Kommunikation begegnet, in dem die
Sperre von Unterbrechungen innerhalb der Prozessfamilie kein
wesentliches und auch ab und zu genutztes Element war. Und sei es nur,
um andere höherstehende Methoden auf einfache Art zu implementieren. Das
heisst ja nicht, dass man auf die anderen Methoden verzichten sollte.
Aber umgekehrt eben auch nicht, dass man Sperren unbedingt vermeiden
sollte.
Wenn solche Sperren also stets schlechter Stil seien, dann scheint es
mir, dass du einen andere Stilbegriff pflegst, als der Rest der Welt.
W.S. schrieb:> Also: das Programm, was den Fifo füllt ....
Watt ein Aufwand, nur um einen "atomaren Zugriff auf eine 16 Bit
Variable" zu ermöglichen.
Da sieht man mal die Vorteile eines 32-Bitters, da ist das alles for
free;)
MfG Klaus
Norbert schrieb:> volatile uint16_t pleaseReadMe;>> uint16_t var1;> uint16_t var2;> do {> var1 = pleaseReadMe;> var2 = pleaseReadMe;> } while (var1 != var2);> Wäre so etwas nicht auch möglich falls man auf gar keinen Fall> Interrupts abschalten will?
Das stellt nicht sicher, dass du einen korrekten Wert erhältst. An
atomaren Zugriffen führt kein Weg vorbei. Und wenn W.S. lange genug in
seine Lernbetty reingeguckt hat, dann wird er möglicherweise erkennen,
dass er auf seine Schreib- und Lesezeiger atomar zugreift (was aber
jetzt auf einem ARM auch kein Kunststück ist).
Peter II schrieb:>> Das stellt nicht sicher, dass du einen korrekten Wert erhältst. An>> atomaren Zugriffen führt kein Weg vorbei.>> Ich wüsste nicht wie ein unkorrekter Wert da durch kommen soll.
Es ist je nach Häufigkeit und Dauer von Interrupts nicht völlig
ausgeschlossen, dass man zweimal den gleichen Fehler kriegt. Nur extrem
unwahrscheinlich.
A. K. schrieb:> Es ist je nach Häufigkeit und Dauer von Interrupts nicht völlig> ausgeschlossen, dass man zweimal den gleichen Fehler kriegt. Nur extrem> unwahrscheinlich.
müsste nicht direkt nacheinander der High und Lowpart geändert werden?
Wenn man 2mal hintereinander den gleichen wert liest, kann keine ISR in
der Zeit den wert geändert haben. Es soll doch nur verhindert das der
Low-Wert nicht zum High-wert einer 16bit variabel passt.
@W.S.
>> Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller>> diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?> Indem der eine Prozeß eben den EXCLUSIVEN Zugriff auf den> Schreibzeiger hat und der andere selbigen auf den Lesezeiger.> Lerne Programmieren und lies nach, wie man einen Fifo bzw.> Ringpuffer schreibt. Herrjenochmal!
Ich würde das ja gerne lernen. Hilf mir doch dabei, anstatt zu meckern.
Wenn ich mit zwei Zeigern Arbeite, dann vergleiche ich sie miteinander,
um festzustellen, ob der Puffer leer ist bzw. wie viele Zeichen drin
sind. Für diesen Vergleich muss ich zwangsläufig auf beide Pointer
zugreifen.
Oder ich nutze eine Variable, die die Anzahl der Zeichen im Puffer
angibt. Das wäre dann aber auch keine exklusive Variable, auch die
müsste ich in beiden Prozessen verwenden.
Wie kann ich das umgehen bzw. besser machen?
Jetzt sag Du mit bitte, wie man es anders (besser?) machen kann.
Peter II schrieb:> Wenn man 2mal hintereinander den gleichen wert liest, kann keine ISR in> der Zeit den wert geändert haben.
Doch. Beispielsweise wenn bei sehr aktiven Interrupts der Zähler
zwischendrin eine komplette Runde gedreht hat und du beim zweiten
Durchlauf der Schleife exakt den gleichen Moment erwischst.
Ja klar, ist extrem unwahrscheinlich. Müssen aber auch nicht komplette
65536 sein. Etwas wahrscheinlicher, wenn jemand zwar 16 Bits verwendet,
aber nur -1, 0, 1 zählt.
Stefan U. schrieb:> Für diesen Vergleich muss ich zwangsläufig auf beide Pointer> zugreifen.
Ja. Aber so lange jeder dieser Zugriff in sich atomar ist, kann man bei
richtiger Reihenfolge der Operationen auf eine Sperre verzichten.
Etwas unglücklich ist deshalb hier der Begriff "Zeiger". Verwendet man
einen 8-Bit Index ist das auch beim AVR möglich, begrenzt aber den
maximal verarbeitbaren Puffer.
A. K. schrieb:> Doch. Beispielsweise wenn bei sehr aktiven Interrupts der Zähler> zwischendrin eine komplette Runde gedreht hat und du beim zweiten> Durchlauf der Schleife exakt den gleichen Moment erwischst.
ok, bei den AVS ist es ja so, dann immer noch mindestens 1Befehl
außerhalb der ISR ausgeführt wird. Damit kann das ja nach Hardware gar
nicht passieren.
> Etwas wahrscheinlicher, wenn jemand zwar 16 Bits verwendet,> aber nur -1, 0, 1 zählt.
dann spielt nur das LowByte eine rolle und ist damit immer richtig.
Peter II schrieb:>> Etwas wahrscheinlicher, wenn jemand zwar 16 Bits verwendet,>> aber nur -1, 0, 1 zählt.> dann spielt nur das LowByte eine rolle und ist damit immer richtig.
Wenn der Programmierer einen nicht-atomaren 16-Bit Typ verwendet, dann
wird der Compiler beide Bytes laden und verwenden.
Klar hätte der Programmierer dann ein Byte nehmen können und damit das
Problem vom Hals gehabt. Sowas kann in auch mal entstehen, wenn man
generischen Code verwendet, der nicht immer mit 8 Bits auskommt.
A. K. schrieb:> Wenn der Programmierer einen nicht-atomaren 16-Bit Typ verwendet, dann> wird der Compiler beide Bytes laden und verwenden.
ja aber da High-Byte ändert sich nicht. Damit passt es immer zum
LowByte.
Peter II schrieb:> ok, bei den AVS ist es ja so, dann immer noch mindestens 1Befehl> außerhalb der ISR ausgeführt wird. Damit kann das ja nach Hardware gar> nicht passieren.
Es sei denn, der Programmierer verwendet verschachtelte Interrupts,
indem er im low-prio Handler die Interrupts freigibt (oder ein RTOS).
Peter II schrieb:> mindestens 1Befehl> außerhalb der ISR ausgeführt wird.
Du brauchst auf AVR nur einen (Assembler-)Befehl um die zweite Hälfte
der ersten Variablen und die erste Hälfte der zweiten Variablen zu
kopieren?
Konrad S. schrieb:> Du brauchst auf AVR nur einen (Assembler-)Befehl um die zweite Hälfte> der ersten Variablen und die erste Hälfte der zweiten Variablen zu> kopieren?
nein, wer hat das gesagt? Durch den einen Befehl wird aber verhindert
das die variabel innerhalb der beiden ausleseversuche einmal komplett
überläuft.
Peter II schrieb:> Durch den einen Befehl wird aber verhindert> das die variabel innerhalb der beiden ausleseversuche einmal komplett> überläuft.
Für den allgemeinen Fall musst du davon ausgehen, dass sich die Daten
durch die Interruptroutine beliebig ändern können.
Konrad S. schrieb:> Peter II schrieb:>> Durch den einen Befehl wird aber verhindert>> das die variabel innerhalb der beiden ausleseversuche einmal komplett>> überläuft.>> Für den allgemeinen Fall musst du davon ausgehen, dass sich die Daten> durch die Interruptroutine beliebig ändern können.
dann beschreibe doch mal bitte genau wo und wann die ISR die Variabel
abändert so das etwas ungültige gelesen wird
> uint16_t var1;> uint16_t var2;> do {> var1 = pleaseReadMe;> var2 = pleaseReadMe;> } while (var1 != var2);
Ich setze die atomic.h sehr gerne ein. Sie macht vieles deutlich
einfacher, kürzer und schneller und man bekommt beim Programmieren auch
keine Knoten im Gehirn von den ganze Würgarounds. Je komplexer eine
Lösung, umso höher ist nämlich die Gefahr von Programmierfehlern.
Ich würde sogar soweit gehen, wenn man sich nicht zu 100% sicher ist,
lieber ein atomic zuviel, als eins vergessen zu haben. Solche Fehler
sind nämlich sehr eklig zu finden.
Niemand fragt danach, ob eine Lösung besonders elegant ist. Aber ob man
sie schnell implementieren kann, sie sicher ist, und möglichst wenig
Seiteneffekte hat.
Daß andere Architekturen andere Mechanismen für konkurrierende Zugriffe
haben, bezweifelt doch niemand. Der AVR hat sie eben nicht und Atmel
wird da auch nichts mehr einbauen.
Wer sich auf dem AVR die Hose mit der Kneifzange anziehen will, kann
dies gerne tun. Nur soll er anderen gefälligst nicht sein persönliche
Meinung aufzwingen. Das geht eindeutig zu weit.
Konrad S. schrieb:> Das Ergebnis ist ein Wert, den die Interruptroutine nie geschrieben hat.
ok, ich sehen es ein.
Es geht als nur sicher, wenn man wie oben a++ mit der Variable rechnet.
Ich sag mal, die AVR-GCC Entwickler sind bestimmt keine Dummköpfe. Und
daß sie die atomic.h implementiert haben, beweist, daß sie sie für
notwendig erachten und daß ihnen keine adäquate bessere Lösung bekannt
ist.
A. K. schrieb:> Ja klar, ist extrem unwahrscheinlich.
Wenn etwas bei der Programmierung absolut verboten ist, dann einen
Fehler zu belassen, weil man glaubt er wäre so unwahrscheinlich dass er
nie auftritt. Abgesehen davon, dass das unter Sicherheitsaspekten
sowieso indiskutabel ist, es stimmt in den meisten Fällen auch nicht.
Ich habe mir vor vielen Jahren auch mal gedacht, das tritt nur alle paar
Jahre mal auf (was an sich ja schon ein Fehler war, da war ich halt noch
jung und unerfahren), dann habe ich das Gerät eingeschaltet und nach 20
sec war der Fehler da. So was ist nie mehr unterlaufen.
Heutige Controller führen Millionen Befehle pro Sekunde aus, da kann die
Wahrscheinlichkeit noch so gering sein, es passiert.
Georg
Man übersieht im täglichen Programmiergeschäft gerne (weil meist
irrelevant), dass Zugriffe auf Daten "nativer" Größe auf allen(?)
Prozessoren und Controllern atomar ablaufen und bei Zugriffen auf
größere Daten ggf. Vorsorgemaßnahmen für atomaren Zugriff getroffen
werden müssen.
Konrad S. schrieb:> dass Zugriffe auf Daten "nativer" Größe auf allen(?)> Prozessoren und Controllern atomar ablaufen
Wobei das noch ein wenig komplizierter wird, wenn man mit mehreren
parallel arbeitenden Cores arbeitet. Das ist in jenen µC Anwendungen,
mit denen man hier im Forum meist zu tun hat, zwar noch nicht
verbreitet, aber arg lange wird es wohl nicht mehr dauern, bis das auch
da Einzug hält.
Interrupts zu sperren bringt da nichts und Befehle, die man bisher als
atomar ansah, sind es vielleicht nicht mehr. Aber genau da greift dann
das vorhin erwähnte load-linked/store-conditional Verfahren. Oder eine
Lib, die darauf aufbaut.
In anderen Bereichen ist das längst Tagesgeschäft, etwa bei PC
Programmierung. Und in "höheren" embedded Umgebungen wird man das wohl
auch schon finden.
Wer mit dem aktuellen RPi seine Steuerung baut, der kann damit schon mal
üben.
A. K. schrieb:> Wobei das noch ein wenig komplizierter wird, wenn man mit mehreren> parallel arbeitenden Cores arbeitet. Das ist in jenen µC Anwendungen,> mit denen man hier im Forum meist zu tun hat, zwar noch nicht> verbreitet, aber arg lange wird es wohl nicht mehr dauern, bis das auch> da Einzug hält.>> Interrupts zu sperren bringt da nichts und Befehle, die man bisher als> atomar ansah, sind es vielleicht nicht mehr. Aber genau da greift dann> das vorhin erwähnte load-linked/store-conditional Verfahren. Oder eine> Lib, die darauf aufbaut.
Caches, Membar usw. hab ich mir annodazumal reingezogen. Es muss ja nur
einmal das grundlegende Verständnis für die Problematik in den Kopf.
Danach ist es relativt leicht, man muss ja nur noch die vorgesehenen
Mechanismen nutzen (und natürlich kennen ;-).
> In anderen Bereichen ist das längst Tagesgeschäft, etwa bei PC> Programmierung. Und in "höheren" embedded Umgebungen wird man das wohl> auch schon finden.
Da muss sich Otto Normalprogrammierer um den ganzen Kleinkram nicht mehr
kümmern. Er meldet seine Wünsche beim Betriebssystem an und das kümmert
sich dann darum. Und weil's so langweilig wär, holt man sich mit
Multithreading die gleichen Probleme in die Anwendungsprogramme. ;-)
> Wer mit dem aktuellen RPi seine Steuerung baut, der kann damit schon mal> üben.
Lieber papp ich dem RPi einen AVR dran. Soll der sich um die Echtzeit
kümmern. Der kann das besser und ich behalte den Überblick.
Konrad S. schrieb:> Danach ist es relativt leicht,
Anfangs. Wird komplizierter, desto tiefer du einsteigst.
Neben atomarem Zeugs hast du flugs auch massive Performanceaspekte am
Hals. Es ist nämlich saublöd, wenn der eine Thread mit seinen Zugriffen
dem anderen Thread die Leistung verhagelt.
Und am oberen Ende wüsste ich ja schon gerne, ob du glaubhaft versichern
kannst, dass du alle Aspekte von Haswells TSX Befehlen relativ leicht
findest. Die haben nämlich direkt damit zu tun. ;-)
A. K. schrieb:> Konrad S. schrieb:>> Danach ist es relativt leicht,>> Anfangs. Wird komplizierter, desto tiefer du einsteigst.>> Neben atomarem Zeugs hast du flugs auch massive Performanceaspekte am> Hals. Es ist nämlich saublöd, wenn der eine Thread mit seinen Zugriffen> dem anderen Thread die Leistung verhagelt.
... nicht zu vergessen: NUMA und processor binding ...
Ausgebremste Software schreiben ist garnichtmal so schwer. ;-)
> Und am oberen Ende wüsste ich gerne, ob du glaubhaft versichern kannst,> dass du alle Aspekte von Haswells TSX Befehlen relativ leicht findest.> ;-)
Ich versichere hiermit glaubhaft ... ;-)
Nein, sicher nicht. Ich schlage mich (beruflich) eher auf der
Applikationsseite mit Multithreading rum. Viele der sonstigen lustigen
Sachen muss ich mir in meiner Freizeit zu Gemüte führen. Und Freizeit
ist ein ziemlich begrenztes Gut, leider, und es gibt noch andere Themen
und Menschen, die Aufmerksamkeit (er)fordern.
Konrad S. schrieb:> Nein, sicher nicht. Ich schlage mich (beruflich) eher auf der> Applikationsseite mit Multithreading rum.
Also genau da, wo TSX interessant ist. Es sei denn du meinst mit der
Applikationsseite, dass du Zahlen in Excel eintippst statt Excel zu
programmieren. ;-)
A. K. schrieb:> Also genau da, wo TSX interessant ist.
Ich bin in der glücklichen Situation, dass meine Threads fast nicht
gegeneinander kämpfen müssen. Die Threads sehen sich kurz beim
Initialisieren von Ressourcen bzw. beim Aufteilen der Daten, dann
rechnet jeder Thread für lange Zeit sein Zeug. Wesentlich wichtiger ist,
dass auf allen Platformen (Windows, MacOS, Linux, Unixe / x86, SPARC,
HP-RISC, PowerPC) die vorhandene CPU-Leistung auch tatsächlich
ausgenutzt wird.
> Es sei denn du meinst mit der> Applikationsseite, dass du Zahlen in Excel eintippst statt Excel zu> programmieren. ;-)
Und Multithreading ist, wenn man gleichzeitig Kaffee trinkt. ;-)