Forum: Compiler & IDEs "Memory Barrier" in C99


von Bruno V. (bruno_v)


Lesenswert?

Ich habe 2 Fragen zu folgendem (sinnlosen) Demonstrationscode für in 
**C99!**
1
int flag, counter;
2
3
interrupt foo(void)
4
{
5
    if(!flag) { /* challenge a: counter muss sicher for flag gesetzt werden */
6
        counter++;
7
        flag = 1;
8
    }
9
}
10
11
int main(void)
12
{
13
    for(;;) {
14
       if(flag) {/* challenge b: Verwendung von counter bevor flag gelöscht wird */
15
           (Verwendung von counter);
16
           flag = 0; 
17
       }
18
       doSomething();
19
    } 
20
}
 * wie stelle ich sicher, dass flag erst "nach" counter gesetzt wird?
   * "asm" dazwischen oder Funktionsaufruf?
   * (implizites) Sprachmittel?
   * oder ist bei beiden ein Lock/Unlock zwingend (z.B. DI/EI)
 * ist "volatile" für flag oder counter zwingend notwendig? 
("doSomething" außerhalb der Übersetzungseinheit)

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Mit `volatile` wirst Du sicher erreichen können, dass der Compiler 
Zugriffe auf die Variablen nicht in der Reihenfolge ändert. Wenn Deine 
Hardware zusätzlich memory barrier braucht, damit dies sicher gestellt 
ist, wird der Compiler diese sicher nicht einfügen.

von Andras H. (andras_h)


Lesenswert?

Volatile ist nicht genug hier. Das macht nur dem Compiler bewusst, dass 
die Variable auch außerhalb der Context geändert werden kann. Deswegen 
wird praktisch beim jeden lese zugriff auf die Variable noch einmal 
gelesen. Bzw schreiben wird auch sofort abgesetzt. Aber Vorsicht, der 
Compiler weiß nichts von dem Cache. Der kann immer noch Probleme machen. 
Und der Compiler weiss auch nicht wie der CPU aufgebaut ist, ob der 
intern nochmal schreibefehle speichern kann. Es ist oft so, dass der RAM 
ECC bits hat, und eine Granularität von 8,16 oder 32 bytes sogar. Das 
heisst, es lohnt sich nicht sofort einen Read Modify Write zu machen, um 
8 bits zu schreiben, weil der über den Bus gehen muss und letztendlich 
werden im RAM doch 8 bytes geändert. Manche CPUs sammeln hier 
schreibbefehle.

In Embedded bereicht hast du sehr oft
MSYNC() und ISYCH() makros im Compiler abstraktion header file.
Diese makros müssen auf den jeweiligen asm befehle gemappt werden. Je 
nach Architektur kann es anders heissen.

MSYNCH macht sicher dass alle Schreibaufträge die bisher ausgeführt 
worden sind auf den Bus geschickt werden. Manche CPUs haben einen 
DataStorage Unit was einige Schreibbefehle speichern kann (wegen 
granularität des RAMs). Das heisst, wenn man etwas schreibt, kann sein 
dass das screiben gar nicht sofort ausgeführt wird. Manche Kontroller 
setzten den nie ab....

ISYNC macht sicher, dass alle Befehle bis zum ISYNC komplett 
abgearbeitet werden. Sprich alles ist durch den Pipeline und ist 
ausgeführt.

Da ich paranoid bin, würde ich so machen:
ISYNC() <= stellt sicher dass alle Befehle davor durch den CPU 
abgearbeitet worden sind
MSYNC() <= stellt sicher dass alle Schreibaufträge auf den Bus gelandet 
sind
ISYNC() <= stellt sicher dass der msynch auch abgearbeitet ist. 
Vermutlich braucht man das nicht, aber schaden kann es ja nicht.
Am Besten noch darauf achten dass der Cache auch write trough ist, und 
beim lesen entweder über nichtgecachte Addresse gehen oder den Cache 
invalidieren.

von Daniel G. (denial)


Lesenswert?

Wenn das normale Variablen sind und der Code auf einem CPU Core läuft, 
reicht volatile. Wenn main und foo auf unterschiedlichen CPU Cores 
laufen können, brauchst du sowas wie atomic_thread_fence aus C11. Auf 
Architekturen wie x86 kompiliert atomic_thread_fence zu nichts. Hast du 
kein C11, musst du das äquivalent mit inline Assembly einfügen.

Sehe gerade C11 hat atomic_signal_fence für den Fall, den man auch mit 
volatile lösen könnte.

: Bearbeitet durch User
von Udo K. (udok)


Lesenswert?

Bruno V. schrieb:
> ist "volatile" für flag oder counter zwingend notwendig?

Volatile nützt dir da nichts.  Du brauchst sowas wie Semaphore statt dem 
"flag" oder EnterCriticalSections/LeaveCriticalSection (Windows) oder du 
muss die Interrupts im kritischen Code ausschalten (Mikrocontroller ohne 
OS).

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.