2009. 7. 9. 16:21

volatile ?

하드웨어 관련 프로그래밍이나 멀티쓰레드 프로그래밍을 하다 보면 volatile 을 접하는 경우가 있다. volatile 에 대해서 약간의 잡담을 하려 한다.

먼저 int 형 변수 하나를 선언한 후, 반복해서 임의의 숫자를 입력하는 코드를 작성해 보자. 이렇게 작성한 후 릴리즈로 빌드 후 printf 에다가 중단점을 설정한 후 디스어셈블리어를 본다.

 원본 소스 릴리즈 빌드 후 디스어셈블리 코드 

int _tmain(int argc, _TCHAR* argv[])

{

        int i = 0;

        i = 1;

        i = 2;

        i = 4;

        i = 6;

        i = 7;

 

        printf("%d", i);

 

        return 0;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

         int i = 0;

         i = 1;

         i = 2;

         i = 4;

         i = 6;

         i = 7;

 

         printf("%d", i);

00401000  push        7   

00401002  push        offset string "%d" (4020F4h)

00401007  call        dword ptr [__imp__printf (4020A0h)]

0040100D  add         esp,8

 

         return 0;

00401010  xor         eax,eax

}

00401012  ret


위에 릴리즈 빌드로 인해 최적화된 코드를 보면 i 에다가 여러 값을 대입하는 코드는 모두 제거되고, 단지 숫자 7을 printf 함수에다가 넘긴다. 어셈블리 코드를 보면 i 를 선언하지도 않는다.

종료 시키고, int 앞에 volatile 을 선언 한후 다시 중단점을 설정하고, 디스어셈블리어를 보자.

 원본 소스  릴리즈 빌드 후 디스어셈블리 코드
 

int _tmain(int argc, _TCHAR* argv[])

{

        volatile int i = 0;

        i = 1;

        i = 2;

        i = 4;

        i = 6;

        i = 7;

 

        printf("%d", i);

 

        return 0;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

00401000  push        ecx 

         volatile int i = 0;

00401001  mov         dword ptr [esp],0

         i = 1;

00401008  mov         dword ptr [esp],1

         i = 2;

0040100F  mov         dword ptr [esp],2

         i = 4;

00401016  mov         dword ptr [esp],4

         i = 6;

0040101D  mov         dword ptr [esp],6

         i = 7;

00401024  mov         dword ptr [esp],7

 

         printf("%d", i);

0040102B  mov         eax,dword ptr [esp]

0040102E  push        eax 

0040102F  push        offset string "%d" (4020F4h)

00401034  call        dword ptr [__imp__printf (4020A0h)]

 

         return 0;

0040103A  xor         eax,eax

}


volatile 선언자 하나 추가햇을 뿐인데, 어셈블리 코드는 완전히 달라졌다. 최적화는 전혀 적용되지 않고 입력한 할당문을 전부 수행하도록 기계어를 생성하였다.

이와 같이, 컴파일러 최적화로 인한 의도하지 않는 상황을 사전에 예방하고, 항상 변수의 주소에서 값을 읽어오고 할당하도록 한다.

실전 사용 예로는, 시리얼 버프에 데이터를 쓰거나 읽을 때 시리얼 버프를 가리키는 주소를 volatile 변수로 선언한 후 원하는 데이터를 시리얼에 전송한다. 이 때 만약 volatile 이 선언되지 않는다면, 수 바이트를 시리얼에 전송하고자 할 때 간혹 최적화로 인해 원하는 데이터가 쓰여지지 않거나 읽혀지지 않을수가 있을 것이다.