Wat is volatile (type qualifier)?

Een type qualifier in C die de compiler vertelt dat de waarde van een variabele buiten het zicht van het programma kan veranderen (door een randapparaat, een interrupt handler enzovoort), of dat de toegangen zelf ertoe doen. Toegangen tot een volatile object worden niet weggelaten of samengevoegd, maar zo vaak en in de volgorde uitgevoerd als ze zijn geschreven. Het wordt gebruikt voor registers van memory-mapped I/O en voor vlaggen die met interrupt handlers worden gedeeld. Het maakt bewerkingen niet atomair en garandeert geen volgorde ten opzichte van andere cores.

Japanse benaming: volatile(型修飾子)

“volatile (type qualifier)” komt voor in 7 oefenvragen uit 6 onderwerpen, bijv. Basis van programmeren in C, Besturingssysteem en hardware, Multicore en synchronisatie.

Vakgebied: Basis van programmeren in C

Wat er zonder volatile gebeurt

In een lus zoals while (flag == 0) { } kan de compiler, als alleen een interrupt handler flag verandert, besluiten dat flag binnen de lus niet kan veranderen, de waarde één keer lezen en een oneindige lus maken. Hetzelfde kan gebeuren in een lus die op een statusregister van een randapparaat wacht, en opeenvolgende schrijfacties naar hetzelfde register kunnen tot één worden samengevoegd. Met volatile worden de lees- en schrijfacties werkelijk zo vaak uitgevoerd als in de code staat.

Hoe de declaratie te lezen

volatile uint32_t *p is een „pointer naar volatile uint32_t”, dus toegangen tot *p worden niet weggelaten. volatile uint32_t *const p voegt de betekenis toe dat de pointer zelf niet kan worden veranderd (hij wijst altijd naar hetzelfde register). Ook macro's die een adres in een pointer omzetten en die derefereren, zoals #define REG (*(volatile uint32_t *)0x40020014), worden veel gebruikt.

Waarvoor volatile niet volstaat

volatile maakt een read-modify-write-bewerking niet atomair, dus een interrupt midden in *reg |= mask; kan de wijziging van een ander bit laten verdwijnen. Een waarde die te breed is om in één keer te lezen of te schrijven, zoals een 32-bits variabele op een 8-bits microcontroller, kan half bijgewerkt worden gelezen als er tussendoor een interrupt optreedt. Voor synchronisatie tussen threads worden mechanismen gebruikt die atomariteit en volgorde garanderen, zoals _Atomic uit C11 of locks.

Nu oefenen: vragen over volatile (type qualifier)

Waarom volstaat het in C niet om een flagvariabele die tussen threads wordt doorgegeven alleen volatile te maken als synchronisatie?

  1. AOmdat een volatile variabele niet meer door andere threads kan worden gelezen
  2. BOmdat volatile alleen compileroptimalisaties onderdrukt en geen ondeelbaarheid van bewerkingen of zichtbare volgorde voor andere cores garandeert
  3. COmdat een volatile variabele op schijf wordt opgeslagen en daardoor traag wordt
Antwoord en toelichting tonen

Antwoord: B. Omdat volatile alleen compileroptimalisaties onderdrukt en geen ondeelbaarheid van bewerkingen of zichtbare volgorde voor andere cores garandeert

Volatile instrueert de compiler lees- en schrijfacties van een variabele niet weg te laten of samen te voegen, en is oorspronkelijk bedoeld voor bewerkingen op hardwareregisters. Het voorkomt niet dat de CPU de volgorde van geheugenbewerkingen wijzigt, en bewerkingen zoals counter++ worden er niet ondeelbaar door. Vanaf C11 en C++11 garanderen _Atomic en std::atomic zowel ondeelbaarheid als geheugenvolgorde.

Veelgestelde vragen

Kunnen const en volatile tegelijk worden gebruikt?

Ja. const volatile betekent „het programma wijzigt het niet, maar het verandert door oorzaken van buiten, dus lees het elke keer opnieuw”, en wordt onder meer gebruikt voor alleen-lezen statusregisters.

Wordt code trager door volatile?

Voor die variabele is de optimalisatie om de waarde in een register te bewaren niet meer mogelijk, dus het aantal toegangen neemt toe en de code kan trager worden. Daarom wordt volatile alleen gebruikt waar het nodig is, zoals bij registers van randapparaten en variabelen die met interrupt handlers worden gedeeld, en niet bij elke variabele.

“volatile (type qualifier)” in de toelichting

Vragen met “volatile (type qualifier)” (7)

  1. Waarom wordt, wanneer zoals hierboven het statusregister van een apparaat via memory-mapped I/O wordt gelezen, volatile gebruikt en wordt dat gebied ingesteld om niet te worden gecachet?
  2. Waarom volstaat het in C niet om een flagvariabele die tussen threads wordt doorgegeven alleen volatile te maken als synchronisatie?
  3. Welke methode wijst bij microcontrollers en dergelijke de registers van randapparatuur toe aan dezelfde adresruimte als het geheugen, zodat ze met gewone geheugenlees- en schrijfinstructies worden bediend?
  4. Wanneer het bovenstaande programma met optimalisatie wordt gecompileerd, wordt led_on() soms niet aangeroepen, ook al treedt de timerinterrupt op. Welke combinatie van oorzaak en tegenmaatregel is juist?
  5. In embedded programma's wordt een variabele die de waarde van een 32-bits randapparaatregister bevat, vaak gedeclareerd met uint32_t uit stdint.h in plaats van unsigned int. Wat is de belangrijkste reden?
  6. Een uitgangsregister van een microcontroller wordt gedeclareerd en gebruikt zoals hierboven. Welke beschrijving van deze declaratie is juist?
  7. Bij het meten van de rekensnelheid met een kleine lus als hierboven bleef de tijd vrijwel 0, ook bij een grote N. Wat is de meest waarschijnlijke oorzaak?

Verwante termen

Variabele Memory-mapped I/O UART Interrupt Microcontroller (MCU) Pointer Interrupt handler Bitveld (bit-field) Bufferoverloop (buffer overflow) Dangling pointer

Naar de woordenlijst