Volatile用法總結

在大多數情況下,把變量緩存在寄存器中是一個非常有價值的優化方法,如果不用的話很可惜。C和C++給你提供了顯式禁用這種緩存優化的機會。如果你聲明變量是使用了volatile修飾符,編譯器就不會把這個變量緩存在寄存器裏——每次訪問都將去存取變量在內存中的實際位置。

1、中斷服務程序中修改的供其它程序檢測的變量需要加volatile;

2、多任務環境下各任務間共享的標誌應該加volatile;

3、寄存器對應的變量值一般最好加上volatile,例如:

volatile UINT32* Register;    // Register is the address of the register

又如:

 *(volatile UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_ICR_HIGH_OFFSET)  = ICRHigh;
*(volatile UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_ICR_LOW_OFFSET)  = ICRLow;

4、當多條指令往同一個寄存器內按照時序寫值時,如果用O2優化,程序只寫入最後一條指令的寫!顯然是錯誤的!

===============

自己的實踐!

用cl test.c /FAcs /Zi /O2編譯下面程序。

#include "stdio.h"

unsigned int q;

int main()
{
//  volatile unsigned int *i;
  unsigned int *i;
  unsigned int p;
 
  p = 0x55aa;

  i = (unsigned int *) 0x310;
  *i = p;
  p = *i;   // 如果沒有volatile,編譯器優化O2認爲此步多餘,在下一步會直接將0x55aa賦給q;

               // 但是如果i是一個硬件寄存器的地址,需要寫入一個值再讀出狀態值,那麼q的值就不是正確的狀態值,而是0x55aa!這顯然是錯誤的。
  
  q = p;
 
  return 0;
}

test_non_volatile.cod:

; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077

 TITLE test.c
 .386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES SEGMENT BYTE USE32 'DEBTYP'
$$TYPES ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT _main
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
sxdata SEGMENT DWORD USE32 'SXDATA'
sxdata ENDS
FLAT GROUP _DATA, CONST, _BSS
 ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif

INCLUDELIB LIBC
INCLUDELIB OLDNAMES

_DATA SEGMENT
COMM _q:DWORD
_DATA ENDS
PUBLIC _main
; Function compile flags: /Ogty
; File e:/temp/test.c
; COMDAT _main
_TEXT SEGMENT
_main PROC NEAR     ; COMDAT

; 7    : //  volatile unsigned int *i;
; 8    :   unsigned int *i;
; 9    :   unsigned int p;
; 10   :  
; 11   :   p = 0x55aa;
; 12   :
; 13   :   i = (unsigned int *) 0x310;

  00000 b8 10 03 00 00  mov  eax, 784  ; 00000310H

; 14   :   *i = p;

  00005 c7 00 aa 55 00
 00   mov  DWORD PTR [eax], 21930 ; 000055aaH

; 15   :   p = *i;    // 忽略此句!
; 16   :  
; 17   :   q = p;

  0000b c7 05 00 00 00
 00 aa 55 00 00  mov  DWORD PTR _q, 21930 ; 000055aaH  //直接賦0x55aa

; 18   :  
; 19   :   return 0;

  00015 33 c0   xor  eax, eax

; 20   : }

  00017 c3   ret  0
_main ENDP
_TEXT ENDS
END

test_volatile.cod:

; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077

 TITLE test.c
 .386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES SEGMENT BYTE USE32 'DEBTYP'
$$TYPES ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT _main
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
sxdata SEGMENT DWORD USE32 'SXDATA'
sxdata ENDS
FLAT GROUP _DATA, CONST, _BSS
 ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif

INCLUDELIB LIBC
INCLUDELIB OLDNAMES

_DATA SEGMENT
COMM _q:DWORD
_DATA ENDS
PUBLIC _main
; Function compile flags: /Ogty
; File e:/temp/test.c
; COMDAT _main
_TEXT SEGMENT
_main PROC NEAR     ; COMDAT

; 7    :   volatile unsigned int *i;
; 8    : //  unsigned int *i;
; 9    :   unsigned int p;
; 10   :  
; 11   :   p = 0x55aa;
; 12   :
; 13   :   i = (unsigned int *) 0x310;

  00000 b8 10 03 00 00  mov  eax, 784  ; 00000310H

; 14   :   *i = p;

  00005 c7 00 aa 55 00
 00   mov  DWORD PTR [eax], 21930 ; 000055aaH

; 15   :   p = *i;
; 16   :  
; 17   :   q = p;

  0000b 8b 00   mov  eax, DWORD PTR [eax]  // 彙編顯式將內存i中的值賦給p
  0000d a3 00 00 00 00  mov  DWORD PTR _q, eax  //q也能正確得到真實的內存中的值

; 18   :  
; 19   :   return 0;

  00012 33 c0   xor  eax, eax

; 20   : }

  00014 c3   ret  0
_main ENDP
_TEXT ENDS
END

 如果我們不加優化選項O2, cod文件如下,可見彙編代碼也是正確的。

; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077

 TITLE test.c
 .386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES SEGMENT BYTE USE32 'DEBTYP'
$$TYPES ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
sxdata SEGMENT DWORD USE32 'SXDATA'
sxdata ENDS
FLAT GROUP _DATA, CONST, _BSS
 ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif

INCLUDELIB LIBC
INCLUDELIB OLDNAMES

_DATA SEGMENT
COMM _q:DWORD
_DATA ENDS
PUBLIC _main
; Function compile flags: /Odt
; File e:/temp/test.c
_TEXT SEGMENT
_i$ = -8      ; size = 4
_p$ = -4      ; size = 4
_main PROC NEAR

; 6    : {

  00000 55   push  ebp
  00001 8b ec   mov  ebp, esp
  00003 83 ec 08  sub  esp, 8

; 7    : //  volatile unsigned int *i;
; 8    :   unsigned int *i;
; 9    :   unsigned int p;
; 10   :  
; 11   :   p = 0x55aa;

  00006 c7 45 fc aa 55
 00 00   mov  DWORD PTR _p$[ebp], 21930 ; 000055aaH

; 12   :
; 13   :   i = (unsigned int *) 0x310;

  0000d c7 45 f8 10 03
 00 00   mov  DWORD PTR _i$[ebp], 784 ; 00000310H

; 14   :   *i = p;

  00014 8b 45 f8  mov  eax, DWORD PTR _i$[ebp]
  00017 8b 4d fc  mov  ecx, DWORD PTR _p$[ebp]
  0001a 89 08   mov  DWORD PTR [eax], ecx

; 15   :   p = *i;

  0001c 8b 55 f8  mov  edx, DWORD PTR _i$[ebp]
  0001f 8b 02   mov  eax, DWORD PTR [edx]
  00021 89 45 fc  mov  DWORD PTR _p$[ebp], eax

; 16   :  
; 17   :   q = p;

  00024 8b 4d fc  mov  ecx, DWORD PTR _p$[ebp]
  00027 89 0d 00 00 00
 00   mov  DWORD PTR _q, ecx

; 18   :  
; 19   :   return 0;

  0002d 33 c0   xor  eax, eax

; 20   : }

  0002f 8b e5   mov  esp, ebp
  00031 5d   pop  ebp
  00032 c3   ret  0
_main ENDP
_TEXT ENDS
END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章