Visual C++ >> Console

volatile


volatile是一個C語言的關鍵字,該關鍵字用來限制編譯器對變數做快取機制、最佳化的使用,在不考慮這個關鍵字前,如果變數比較常使用到時,則該變數就不會被更新到記憶體,等到最後再寫回記憶體即可,這樣是比較有效的運用快取機制,但是,卻會造成資料不同步的問題,尤其是在同步存取的時候,可能有另一個副程式取存到尚未更新完畢的記憶體資料,這就會導致不同步的問題,所以在變數名稱前加上volatile關鍵字就可以保證存取該變數時,CPU會做記憶體與快取資料的更新動作,達到正確存取資料的內容。

另外一個問題則是硬體初始化問題,對硬體初始化時,可能需要重覆送出相同的資料給硬體,但是最佳化時,就會變成只有執行一行送出資料的命令給硬體,因為編譯器只會對程式最佳化,編譯器不會考慮到硬體使用的時機問題,這個時候只要在變數前加上volatile關鍵字就可以告知編譯器不要對此變數做最佳化處理,達到送出相同命令給硬體的需求。

同步問題
如下例子,中斷副程式可能會不定時進入,但是,在main副程式卻是無窮迴圈的等待,所以運用快取機制時,bFlag可能不會被更新到記憶體,這樣會導致main副程式無法繼續運作

unsigned char bFlag=0;

void Interrupt(void) interrupt 1 
{
  bFlag = 0x00;
}

int main(int argc, char* argv[])
{
  bFlag = 0xff;
  while(bFlag);
 
  return 0;
}

翻譯成組合語言

4: void Interrupt(void) interrupt 1 
5: { 
6:     bFlag = 0;  
C:0x0007    750D00   MOV      bFlag(0x0D),#0x00
7: } 
  
5: int main(int argc, char* argv[]) 
C:0x009A    8E08     MOV      0x08,R6
C:0x009C    8F09     MOV      0x09,R7
C:0x009E    8B0A     MOV      0x0A,R3
C:0x00A0    8A0B     MOV      0x0B,R2
C:0x00A2    890C     MOV      0x0C,R1
6: { 
7:         bFlag = 0xff; 
C:0x00A4    750DFF   MOV      bFlag(0x0D),#0xFF
8:     while(bFlag); 
9:       
C:0x00A7    E50D     MOV      A,bFlag(0x0D)
C:0x00A9    70FC     JNZ      C:00A9
10:     return 0; 
C:0x00AB    E4       CLR      A
C:0x00AC    FE       MOV      R6,A
C:0x00AD    FF       MOV      R7,A
12: } 

從反組譯的結果可以看到C:0x00A9是一個無窮迴圈,也就是while(bFlag);這一行程式,但是,可以看到A暫存器沒有再度被更新,也就是沒有從記憶體更新到快取,所以,在中斷副程式改變的數值,將無法順利的被main副程式判斷到。

重複執行問題
如下例子,重複的程式碼在做完最佳化後,只會執行一行而已

#define CommandPort ((unsigned char *)0)[0xFF03]

int main(int argc, char* argv[])
{
  *CommandPort = 0xaa;
  *CommandPort = 0xaa;
  *CommandPort = 0xaa;
  return 0;
}

這個時候,只要在變數前加上volatile關鍵字,就可以讓這三行程式順利的被執行到。


返回上一頁