volatile 에 대한 의문..

Interest/Technic Magazine 2005/05/27 10:58
출처 : http://c-study.com


먼저 다수를 상대하는듯한 말투를 먼저 이해해주시길....
먼저 다음에 물음에 답해보시라...
혹시 당신이 C로짠 임베디드 코드에서 다음과 같은 경우를 경험한적이 있는가??

* 옴티마이즈 옵션을 켜기 전까지는 코드가 잘 동작한다
* 어떤 인터럽트를 디스에이블(disable)시켜논 동안에는 코드가 잘 동작한다
* RTOS 가 탑재된 멀티태스킹 시스템에서 어떤 태스크(TASK)가 인에블(enable) 되기전까지는 태스크가 잘 동작한다..


만약 위의 물음에 "네(yes)"라고 대답한다면 그건 바로 당신이 volatile라는 C keyword를 사용하지 않았기 때문이다..
이건 비단 당신혼자만의 문제는 아니다..
많은 프로그래머들이 volatile 라는 키워드에 대해서 어설프게 잘못알고 있거나 제대로 사용하지 않고 있다..

이건 그리 놀랄만한 일이 아닌데 그건 바로 많은 C 책이 이점에 관해서 너무하리만큼 무심하기 때문이다..

volatile 는 변수를 선언할때 같이 사용하는 키워드다.
volatile를 사용함으로인해 컴파일러에게 volatile과 함께 선언된 변수는 언제 어느때든지 값이 바뀔수 있다는 것을 말한다.


구체적인 사용예를 들기전에 먼저 volatile에 대한 문법적인 사항을 알아보자..
volatile 변수를 사용하기 위해 volatile라는 키워드를 정의된 변수의 데이터 타입 앞 또는 뒤에 명시하면 된다..
다음과 같이 말이다

volatile int foo;
int volatile foo;

자, 그럼 포인터에서는 어떻게 될까??
포인터에서 뭔가 특별한점이 있다고 생각하는가??

천만의 말씀이다..
포인터라 해서 별반 다를게 없다..
포인터 역시 다음과 같이 하면된다..

volatile int *foo;
int volatile *foo;

그럼 이것역시 같다고 생각하는가??


int * volatile foo;
int volatile * volatile foo;

이건 숙제다..

마지막으로 volatile을 struct나 union에 적용시켜버리면 struct나 union의 모든 내용들은 volatile 이다..
만약 위와같은 경우를 원하지 않는다면 어떻게 할것인가??
struct나 union 멤버에게 개별적으로 사용하면 된다..
자, 그럼 본격적인 사용법을 알아보자...

어떤 변수들이 예고없이 값이 바뀔수 있을 가능성이 있는경우에는 volatile로 선언해야 한다..
사실상 다음의 3가지 타입의 변수들이 바뀔수 있다.

* memory-mapped periherral registers
* 인터럽트 서비스 루틴에 의해 수정되는 전역변수
* 멀티 태스킹 또는 멀티 쓰레드 에서 사용되는 전역변수

그럼 먼저 첫번째 항목에 대해서 좀더 자세히 알아보자.


임베디드 시스템에서는 진보되고 복잡한 실질적인 주변디바이스(periherial) 를 포함하게 된다.
이런 peripherial들은 프로그램 흐름과 비동기적으로 값들이 변하는 레지스터들을 가지고 있는 경우가 대부분이다..
매우 간단한 예로 0x1234 address에 위치한 8비트 status 레지스터가 있다고 가정하고 생각해보자.
만약 이 레지스터가 0이 아닌 값을 가질때까지 이 레지스터를 폴링(polling)한다고 가정해보자.
그럼 당신은 분명히 다음과 같이 코드를 작성할것이다..

INT8U *ptr = (INT8U *)0x1234;

// wait for register to become non-zero
while (*ptr == 0);
// Do something else

만약 당신이 옴티마이즈 옵션을 켰다면 위의 코드는 제대로 동작하지 않을 확율이 굉장히 높다..
왜냐하면 컴파일러는 당신이 작성한 코드에 대해서 다음과 같은
어셈블러를 생성할것이다..
반드시 유심히 보길 바란다.. 중요하다..

move ptr, #0x1234
move a, @ptr
loop bz loop

자, 한번 분석해보자..
컴파일러는 굉장히 똑똑하게 어셈블리 코드를 생성한것을 볼수 있다.
첨에 한번반 0x1234를 억세스해서 값을 로딩한 이후로 두번다시는 0x1234를 억세스 하지 않는다.
두번째 코드에서 볼수 있듯이 값은 accumulator에 이미 로딩이 되있기 때문에 값을 재 로딩할 필요가 없다고 컴파일러는 판단하기 때문이다.
왜냐하면 값은 항상 같다고 보기 때문이다..
그러므로 3번째 라인에 의해 당신이 작성한 코드는 무한루프에 빠지게 된다.

정작 우리가 원하는 동작을 하기 위해서는 위의 코드를 다음과 같이 수정해야 한다.

INT8U volatile * ptr = (INT8U volatile *)0x1234;

그럼 컴파일러는 이제 다음과 같이 어셈블러를 생성할것이다..

mov ptr, #0x1234
loop mov a, @ptr
bz loop

자, 어떤가??
드뎌 당신이 원하는 결과를 얻게 될것이다...

자, 그럼 인터럽트 서비스 루틴의 경우에 대해서 생각해보자..
종종 인터럽트 서비스 루틴은 메인함수에서 테스트하는 변수를 셋팅하게 된다..
예를 들어 시리얼 포트 인터럽트는 각각에 수신한 캐릭터에 대해 ETX 캐릭터가 수신됬는지를 테스트한다고 가정해보자..
만약 ETX가 수신되면 인터럽트 서비스 루틴은 전역 플래그를 셋팅할것이다.

불완전한 코드를 다음에 보이겠다..

int ETXRcvd = FALSE;

void main (void)
{
...
while (!ETXRcvd) {
// what
}
...
}

interrupt void RxISR (void)
{
...
if (rx_char == ETX) {
ETXRcvd = TRUE;
}
...
}

옵티마이즈 옵션을 꺼논동안에는 코드가 올바르게 작동할 것이다..
그러나, 그렇지 않을 경우에는??
문제는 컴파일러는 ETXRcvd가 인터럽트 서비스 루틴에 의해서 값이 바꼈을 경우 이를 알수 없는 경우가 생긴다..
위에 peripherial 예세서 들었듯이 !EXTRcvd 는 항상 참이기 때문에 while 루프를 절대 벗어날수 없는 경우가 생길수도 있다..
게다가 심지어는 이런 이유로 인해 루프 이후에 코드들은 옵티마이즈에 의해 제거 되버릴수도 있다..
만약 당신이 운좋은 놈이라면 당신의 컴파일러는 이런 문제에 대해서 경고 메세지를 보내게 될것이다..
그렇지 않고 당신이 운좋은 놈이 아니거나 컴파일러가 제공하는 경고 메세지가 때로는 얼마나 무서운것인지를 경험해보지 못했다면 어떻게 될까??
말안해도 알리라 본다..
모른다면???


그건 나도 모르는 일이다...

이제 마지막으로 멀티 쓰레드또는 멀티 태스킹 어플리케이션 경우를 생각해 봐야 되는데 이 문제에 대해서는 여러분들의 호응이나 요청이 있을시 논의해 보도록 하겠다..
미안한 말이지만 요즘 난 극도로 심신이 지쳐있기 때문에 더 이상 쓸 힘이 지금은 없다..
솔직히 말해 쓰러질것만 같다...
지금은 밤 12시... 하루종일 일하고 이제는 자야할 시간이다..
그래도 내가 억지로 쓰는 이유는 비록 내가 암을 써보지 않았고 현재의 자세한 상황을 모르기때문에 딱히 뭐라고 대답을 해줄순 없지만 혹시나 상일이 한테참고가 될까 해서 쓰는것이다..

상일아.... 나 고맙냐???

혹시라도 내 글에 리플이 달리거나 계속해 달라는 요청이 있으면 그때는 반드시 계속하리라....
오늘은 이쯤해서 결말을 짖도록 하겠다..
앞으로 우리가 임베디드 시스템 프로그램을 하면서 남용하지는 않더라도 volatile 키워드를 애용하도록 하자....


만약의 경우라도 대비해서 말이다....

임인건은 그의 저서 "터보씨 정복"에서 문제가 될 소지가 있는부분에 대해서는 할수만 있거든 미연에 사태를 막아놓고 아예 싹을 잘라버릴것을 심신 당부하고 있고

임베디드 프로그래밍 관련 서적 집필로 유명한 Michael Barr는 그이 저서 "Programming Embedded Systems in C and C++"에서 peripherial을 억세스 할때는 잔말 말고 무조건 volatile 키워드를 사용하라고 우리에게 협박(?)까지 하고 있다..

나의 작은 글이 여러분들의 임베디드 시스템 프로그래밍에 있어서 큰 도움이 되길 바란다..

- from iw2sayno -
top

Trackback Address :: http://unizard.cafe24.com/tt/trackback/64

Write a comment