Forum: Compiler & IDEs volatile einzelner Elemente im struct


von Daniel (Gast)


Lesenswert?

Hallo,

ist es möglich, dass man einzelne Elemente eines structs als volatile 
kennzeichnet?
1
struct {
2
   uint8_t bit_1: 1;
3
   volatile uint8_t bit_2: 1;
4
   uint8_t bit_3_4 :2; 
5
} Status = { 0, 1, 0, 0,1 }; //  Initialisierung

oder muss das ganze struct als volatile gekennzeichnet werden?
1
volatile struct {
2
   uint8_t bit_1: 1;
3
   uint8_t bit_2: 1;
4
   uint8_t bit_3_4 :2; 
5
} Status = { 0, 1, 0, 0,1 }; //  Initialisierung

Bin ich richtig der Annahme, dass in letzterem Fall, ALLE Elemente 
volatile sind und oben nur das einzelne bit?

von Oliver (Gast)


Lesenswert?

Die Frage stellt sich bei real existierenden Prozessoren und Bits 
innerhalb eines einzelnen Bytes eigentlich gar nicht.

Oliver

von Daniel (Gast)


Lesenswert?

Ich programmiere einen Atmega8.
Entnehme ich der Aussage, dass dann sowieso das ganze Byte als volatile 
deklariert wird?

von Timmo H. (masterfx)


Lesenswert?

Beide Wege sind möglich.

Im ersten Fall ist nur das bit_2 volatile. Im zweiten Fall sind es alle 
Elemente des Structs volatile. So ist es zumindest bei normalen 
"Variablen". Möglicherweise ist das bei Bitfeldern auch anders und es 
ist immer das gesamte Byte/Wort wo ein oder mehrere Bits eines Wertes 
drin sind volatile.

Da dein Bitfeld in summe aber < uint8_t ist, ist es vermutlich sogar 
besser es komplett volatile zu machen, ansonsten kommt der Compiler 
vielleicht noch auf die Idee für bit_1 und bit_3_4 ein eigenen Speicher 
zu reservieren.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich hab's mal mit dem GCC ausprobiert, und das Ergebnis entspricht den
Erwartungen:

Im ersten Fall wird nur bit_2 als volatile behandelt, im zweiten Fall
alle drei Elemente.

Ich habe jetzt nicht nachgeschaut, ob der Standard dieses Verhalten
genau so vorschreibt, wüsste aber nicht, was dagegen sprechen sollte.

Timmo H. schrieb:
> Da dein Bitfeld in summe aber < uint8_t ist, ist es vermutlich sogar
> besser es komplett volatile zu machen, ansonsten kommt der Compiler
> vielleicht noch auf die Idee für bit_1 und bit_3_4 ein eigenen Speicher
> zu reservieren.

Zumindest beim GCC gibt es diesbezüglich keinen Grund zur Besorgnis: In
den beiden Beispielen belegt die Struktur jeweils nur ein einzelnes
Byte. Da Zugriffe auf Bitfelder wegen der Bit-Maskiereri und -Schieberei
länger dauern als gewöhnliche uint8_t-Zugriffe, ist es schon sinnvoll,
nur diejenigen Felder volatile zumachen, die dies wirklich erfordern.

von Andreas B. (andreas_b77)


Lesenswert?

Yalu X. schrieb:
> Ich hab's mal mit dem GCC ausprobiert, und das Ergebnis entspricht den
> Erwartungen:
>
> Im ersten Fall wird nur bit_2 als volatile behandelt, im zweiten Fall
> alle drei Elemente.
>
> Ich habe jetzt nicht nachgeschaut, ob der Standard dieses Verhalten
> genau so vorschreibt, wüsste aber nicht, was dagegen sprechen sollte.

Das würde aber nur auf Architekturen funktionieren, die wirklich auf 
einzelne Bits zugreifen können. Wie sollte ansonsten etwa bit_1 
beschrieben werden, ohne dass auf bit_2 zugegriffen wird?

Das hat vor kurzem sogar handfeste Probleme im Linux-Kernel gegeben, 
weil gcc bei Bitfields Code generiert, der auch benachbarte Felder 
beschreiben kann. Siehe http://lwn.net/Articles/478657/

von Yalu X. (yalu) (Moderator)


Lesenswert?

Andreas B. schrieb:
> Das würde aber nur auf Architekturen funktionieren, die wirklich auf
> einzelne Bits zugreifen können. Wie sollte ansonsten etwa bit_1
> beschrieben werden, ohne dass auf bit_2 zugegriffen wird?

Man muss sich natürlich dessen bewusst sein, dass Bitfeldzugriffe von
sich aus i.Allg. nicht atomar sind, d.h. man muss sie ggf. atomar machen
(bspw. durch das Sperren von Interrupts).

Bei dem von dir verlinkten Beispiel war es so, dass der Compiler Code
generiert hat, der zum Schreiben eines einzelnen Bits unnötig viele
Bits (nämlich 64) beeinflusst hat, obwohl wegen des "unsigned int" nur
32 zu erwarten gewesen wären (selbst 8 hätten schon gereicht und wären
auch möglich gewesen). Die Linux-Entwickler sind im konkreten Fall davon
ausgegangen, dass eine spezielle Zugriffssteuerung unnötig ist, was sich
als Problem herausgestellt hat.

Dieses problematische Verhalten konnte ich bei dem Test mit dem AVR-GCC
aber nicht feststellen. Selbst wenn die Bitfelder als "int" oder
"unsigned int" deklariert werden, werden immer nur diejenigen Bytes
angetastet, die das betreffende Bitfeld auch tatsächlich enthalten.

von Andreas B. (andreas_b77)


Lesenswert?

Yalu X. schrieb:
> Man muss sich natürlich dessen bewusst sein, dass Bitfeldzugriffe von
> sich aus i.Allg. nicht atomar sind

Und eben das macht Bitfields, in denen ein volatile Element mit anderen 
(ob volatile oder nicht) im kleinsten adressierbaren Datenwort (generell 
Byte) kombiniert wird, prinzipiell unmöglich korrekt implementierbar.

von (prx) A. K. (prx)


Lesenswert?

Weshalb nicht? "volatile" hat nichts mit atomaren Zugriffen zu tun!

von Yalu X. (yalu) (Moderator)


Lesenswert?

Andreas B. schrieb:
> Und eben das macht Bitfields, in denen ein volatile Element mit anderen
> (ob volatile oder nicht) im kleinsten adressierbaren Datenwort (generell
> Byte) kombiniert wird, prinzipiell unmöglich korrekt implementierbar.

Definiere "korrekt".

Auch ein 16-Bit-Integer auf einer 8-Bit-CPU erlaubt keinen atomaren
Zugriff. Bei der Programmierung mit Interrupts und Threads muss dies
beachtet werden, sonst wird evtl. Datenmüll produziert. Das ist aber
dann kein Fehler des Compilers, der Hardware oder des C-Standards,
sondern ganz alleine des Programmierers.

Ähnlich verhält es sich mit den Bitfeldern.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:

> Bei dem von dir verlinkten Beispiel war es so, dass der Compiler Code
> generiert hat, der zum Schreiben eines einzelnen Bits unnötig viele
> Bits (nämlich 64) beeinflusst hat, obwohl wegen des "unsigned int" nur
> 32 zu erwarten gewesen wären (selbst 8 hätten schon gereicht und wären
> auch möglich gewesen). Die Linux-Entwickler sind im konkreten Fall davon
> ausgegangen, dass eine spezielle Zugriffssteuerung unnötig ist, was sich
> als Problem herausgestellt hat.
>
> Dieses problematische Verhalten konnte ich bei dem Test mit dem AVR-GCC
> aber nicht feststellen. Selbst wenn die Bitfelder als "int" oder
> "unsigned int" deklariert werden, werden immer nur diejenigen Bytes
> angetastet, die das betreffende Bitfeld auch tatsächlich enthalten.

Da gabs vor kuzen einen lääämglichen Thread zu in den MLs,
siehe "Memory corruption due to word sharing":

http://gcc.gnu.org/ml/gcc/2012-02/threads.html#00005

Da ist auch Beispielcode anbei und Diskussion zwischen den Linuxern und 
GCClern und C-Standard rauf und runter ;-)

von (prx) A. K. (prx)


Lesenswert?

Niedlicher Thread, auf den du da verlinkst. ;-)

Es gibt eben Dinge zwischen Himmel und Erde, die sich in C nicht sauber 
lösen lassen. Wenn ein Zugriff auf volatile Variablen nur dort zulässig 
ist, wo er im Quelltext drinsteht, dann sind volatile Bitfelder 
grundsätzlich nur zulässig, wenn man die Zugriff darauf sauber trennen 
kann. Damit aber sind sie so gut wie sinnlos.

Denn in einem Byte aus 2 4-Bit Feldern wie in
  struct S { unsigned a:4, b:4; }
mit einer Verwendung in Form von
  volatile S x;
ist bei auf mindestens Bytes operierender Hardware unvermeidlich, dass 
ein Zugriff auf a auch einen Zugriff auf b durchführt, was aber im 
Quelltext nicht drinsteht. Das ist zwar in diesem Fall ein eher 
theoretisches Problem als ein praktisches, aber unlösbar.

Bliebe eigentlich nur die Möglichkeit, bei volatilen gepackten Daten 
freundlich darauf hinzuweisen, dass man sich in Teufels Küche begibt. 
Auch seitens des Compilers.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:

> Niedlicher Thread, auf den du da verlinkst. ;-)

Jo, beim Lesen dachte ich mir auch "Popcorn!!!" :-)

> Denn in einem Byte aus 2 4-Bit Feldern wie in
>   struct S { unsigned a:4, b:4; }
> mit einer Verwendung in Form von
>   volatile S x;
> ist bei auf mindestens Bytes operierender Hardware unvermeidlich, dass
> ein Zugriff auf a auch einen Zugriff auf b durchführt, was aber im
> Quelltext nicht drinsteht. Das ist zwar in diesem Fall ein eher
> theoretisches Problem als ein praktisches, aber unlösbar.

Der C-Standard hat das dadurch "gelöst", indem er volatile 
"implementation definded" macht (C90 6.5.3, C99 6.7.3).

Neben den Unterschieden zwischen C und C++, siehe

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Volatiles.html#C_002b_002b-Volatiles

gibt's dann noch weitere, hausbackene Verwendungen von volatile wie "asm 
volatile", volatile Funktionszeiger (noreturn-Semantik) und "register 
volatile asm" (wird genauso behandelt wie nich-volatiles "register 
asm").

Und dann gibt's auch noch -fstrict-volatile-bitfields

http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fstrict_002dvolatile_002dbitfields-2305

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> Und dann gibt's auch noch -fstrict-volatile-bitfields

Das scheint mir die Lösung für dieses Problem zu sein:

Andreas B. schrieb:
> Das hat vor kurzem sogar handfeste Probleme im Linux-Kernel gegeben,
> weil gcc bei Bitfields Code generiert, der auch benachbarte Felder
> beschreiben kann. Siehe http://lwn.net/Articles/478657/

Johann L. schrieb:
> Da gabs vor kuzen einen lääämglichen Thread zu in den MLs,
> siehe "Memory corruption due to word sharing":
>
> http://gcc.gnu.org/ml/gcc/2012-02/threads.html#00005

Die Option scheint im November 2011 (GCC 4.6) eingeführt worden zu sein,
die verlinkten Diskussionen fanden im Februar 2012 statt. Waren die
Leute im Februar einfach noch nicht auf dem neuesten GCC-Stand, haben
sie die neue Option noch nicht gekannt oder wirft die Option neue
Probleme auf, so dass man sie doch nicht als Lösung ansehen kann?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Johann L. schrieb:
>> Und dann gibt's auch noch -fstrict-volatile-bitfields
>
> [...]
>
> Die Option scheint im November 2011 (GCC 4.6) eingeführt worden zu sein,
> die verlinkten Diskussionen fanden im Februar 2012 statt. Waren die
> Leute im Februar einfach noch nicht auf dem neuesten GCC-Stand, haben
> sie die neue Option noch nicht gekannt oder wirft die Option neue
> Probleme auf, so dass man sie doch nicht als Lösung ansehen kann?

Die technischen Details kannst du in der Compiler-Bild nachlesen:

gcc-patches:
http://www.google.de/#sclient=psy-ab&q=site:gcc.gnu.org%2Fml%2Fgcc-patches+strict+volatile+bitfields

gcc:
http://www.google.de/#sclient=psy-ab&q=site:gcc.gnu.org%2Fml%2Fgcc+strict+volatile+bitfields

Trivial ist's jedenfalls nicht — wie alles in GCC

von Andreas B. (andreas_b77)


Lesenswert?

Yalu X. schrieb:
> Die Option scheint im November 2011 (GCC 4.6) eingeführt worden zu sein,
> die verlinkten Diskussionen fanden im Februar 2012 statt. Waren die
> Leute im Februar einfach noch nicht auf dem neuesten GCC-Stand, haben
> sie die neue Option noch nicht gekannt oder wirft die Option neue
> Probleme auf, so dass man sie doch nicht als Lösung ansehen kann?

Linux muss auch ältere Compiler unterstützen, wenn es sich nur mit dem 
aktuellsten gcc frisch vom GNU ftp-Server übersetzen ließe, gäbe es sehr 
viele sehr unglückliche Leute… Zudem neue Versionen ja auch neue Bugs 
einführen können.

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.