Volatile用法总结

来源:互联网 发布:我要买微信木马软件 编辑:程序博客网 时间:2024/05/16 01:06

在大多数情况下,把变量缓存在寄存器中是一个非常有价值的优化方法,如果不用的话很可惜。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

原创粉丝点击