Bir tanımı volatile
volatile
derleyiciye değişkenin değerinin derleyici bilmeden değişebileceğini söyler. Dolayısıyla, derleyici, değerin değişmediğini kabul edemez, çünkü C programı değişmemiş gibi görünüyor.
Öte yandan, değişkenin değerinin, derleyicinin bilmediği bir yerde gerekli olabileceği (okunabileceği) anlamına gelir, bu nedenle değişkene yapılan her atamanın aslında bir yazma işlemi olarak yapıldığından emin olması gerekir.
Davaları kullanın
volatile
ne zaman gereklidir
- donanım kayıtlarını (veya hafıza eşlemeli G / Ç'yi) değişken olarak temsil eder - kayıt asla okunmasa bile, derleyici sadece "Aptal programcı" değerini belirten yazma işlemini atlamamalıdır. bir daha asla okuyamayacak. Yazmayı ihmal edersek bile farketmeyecek. " Tersine, program değişkene hiçbir zaman bir değer yazmasa bile, değeri yine de donanım tarafından değiştirilebilir.
- yürütme bağlamları arasında değişkenleri paylaşma (örneğin ISR'ler / ana program) (bkz. @ kkramo'nun cevabı).
Etkileri volatile
Bir değişken bildirildiğinde volatile
, derleyici program kodundaki her bir atama işleminin gerçek bir yazma işlemine yansıdığından ve program kodundaki her okunuşun (mmaplanan) bellekten gelen değeri okuduğundan emin olmalıdır.
Uçucu olmayan değişkenler için, derleyici değişkenin değerinin ne zaman / ne zaman değiştiğini bildiğini ve kodu farklı şekillerde optimize edebileceğini bildiğini varsayar.
Birincisi, derleyici değeri CPU yazmaçlarında tutarak belleğe okuma / yazma sayısını azaltabilir.
Örnek:
void uint8_t compute(uint8_t input) {
uint8_t result = input + 2;
result = result * 2;
if ( result > 100 ) {
result -= 100;
}
return result;
}
Burada derleyici muhtemelen result
değişkene RAM tahsis etmeyecek ve ara değerleri hiçbir zaman bir CPU kaydında saklayamayacak.
Eğer result
uçucu olan, her cereyan edişinde result
C kodu derleyici gerektirecektir daha düşük bir performansa yol, RAM için bir erişim (ya da bir I / O bağlantı noktası) ifa edilir.
İkincisi, derleyici, performans ve / veya kod boyutu için geçici olmayan değişkenler üzerindeki işlemleri yeniden sipariş edebilir. Basit örnek:
int a = 99;
int b = 1;
int c = 99;
yeniden sipariş edilebilirdi
int a = 99;
int c = 99;
int b = 1;
bu, bir assembler komutunu kaydedebilir, çünkü değerin 99
iki kez yüklenmesi gerekmez.
Eğer a
, b
ve c
uçucu olan derleyici programa verilmiştir tam olarak sırayla değerlerini atamak talimatları yayarlar gerekir.
Diğer klasik örnek ise şöyle:
volatile uint8_t signal;
void waitForSignal() {
while ( signal == 0 ) {
// Do nothing.
}
}
Eğer bu durumda signal
olmasaydı volatile
, derleyici bunun while( signal == 0 )
sonsuz bir döngü olabileceğini düşünürdü (çünkü döngü içindekisignal
kodla asla değiştirilmeyecekti ) ve eşdeğerini oluşturabilirdi.
void waitForSignal() {
if ( signal != 0 ) {
return;
} else {
while(true) { // <-- Endless loop!
// do nothing.
}
}
}
volatile
Değerlerin ele alınmasında dikkate alın
Yukarıda belirtildiği gibi, bir volatile
değişken gerçekte gerekenden daha sık erişildiğinde performans cezası verebilir. Bu sorunu hafifletmek için, değeri gibi geçici olmayan bir değişkene atayarak değeri "geçici olarak kaldırabilirsiniz".
volatile uint32_t sysTickCount;
void doSysTick() {
uint32_t ticks = sysTickCount; // A single read access to sysTickCount
ticks = ticks + 1;
setLEDState( ticks < 500000L );
if ( ticks >= 1000000L ) {
ticks = 0;
}
sysTickCount = ticks; // A single write access to volatile sysTickCount
}
Eğer çabuk olmasını istediğiniz yerdir ISR yıllarda özellikle faydalı olabilir mümkün olduğunca aynı donanım veya bellek birden çok kez erişen değil sen değer, ISR çalışırken değişmeyecek çünkü gerekli değildir biliyorum. Bu, ISR'nin sysTickCount
yukarıdaki örnekte olduğu gibi değişken için değerlerin 'üreticisi' olması durumunda yaygındır . Bir AVR'de işlevin doSysTick()
bellekte aynı dört bayta (dört komut = erişim başına 8 CPU çevrimi sysTickCount
) yalnızca iki kez yerine beş veya altı kez erişmesi özellikle acı verici olacaktır , çünkü programcı değerin değişmeyeceğini bilecektir doSysTick()
koşarken başka bir koddan değiştirilemez .
Bu hileyle, derleyicinin geçici olmayan değişkenler için yaptığı aynı şeyi yaparsınız, yani yalnızca gerektiğinde bunları bellekten okuyabilir, değeri bir süre için bir kayıt defterinde tutar ve yalnızca gerektiğinde belleğe geri yazar. ; ama bu sefer sen okuduğunda / yazma eğer / iyi derleyici daha biliyor olmalı gerçekleşmesi Bu optimizasyon görevden derleyici rahatlatmak, böylece onu kendi başınıza yapmak.
Sınırlamaları volatile
Atomik olmayan erişim
volatile
yok değil çok kelimeli değişkenlere atomik erişim sağlar. Bu gibi durumlarda, kullanmaya ek olarak , diğer yollarla karşılıklı dışlama sağlamanız gerekecektir volatile
. AVR, kullanmak olabilir ATOMIC_BLOCK
gelen <util/atomic.h>
veya basit cli(); ... sei();
aramaları. İlgili makrolar da, erişim sırası söz konusu olduğunda önemli olan bir bellek bariyeri görevi görür:
İcra emri
volatile
yalnızca diğer değişken değişkenler için katı yürütme emri uygular. Bu, örneğin
volatile int i;
volatile int j;
int a;
...
i = 1;
a = 99;
j = 2;
garantilidir ilk için atamak 1 i
ve sonra 2 atamak j
. Ancak, aralarında atanacak garanti edilmeza
; derleyici bu atamayı kod pasajından önce veya sonra, temelde ilk (okunabilir) okunana kadar herhangi bir zamanda yapabilir a
.
Yukarıda belirtilen makroların hafıza bariyeri olmasaydı, derleyicinin çevrilmesine izin verilirdi
uint32_t x;
cli();
x = volatileVar;
sei();
için
x = volatileVar;
cli();
sei();
veya
cli();
sei();
x = volatileVar;
(Bütünlük adına Aslında kullanımını ortadan kaldırabilir, sei / cli makrolar anlattığına gibi bu bellek engelleri söylemeliyim volatile
eğer, bütün erişimler bu engellerin ile parantez edilir.)