본문 바로가기

CS

Volatile 지정자

시그널 핸들러 관련해서 CS:APP 책을 읽다가 Volatile 지정자를 쓰는 이유가 정확히 와닿지 않아서 정리해보려고 한다.

검색하면 알 수 있듯이 volatile 지정자는 사용할 때마다 항상 메모리에 접근한다. 이 변수는 언제든 값이 바뀔 수 있으니 메모리에 항상 접근하라고 컴파일러에게 알려주는 것이다.  간단한 예시를 들어서 설명해보자면  

int i=10;
while (i<10)
	i++;
printf("%d \n" , i);

위와 같은 코드는 다음과 같이 최적화가 된다.

int i=10;
printf("%d \n" ,i);

컴파일러가 알아서 위의 코드처럼 처리를 한다는 것이다. 그렇다면 왜 굳이 volatile을 써서 계속 메모리에 접근하게 하는 것인가. 보통 volatile 키워드는 3가지 경우에 많이 사용된다.

  1. MMIO(memory mapped I/O)
  2. interupt service routine
  3. multi thread environment

signalsuspend 예제를 통해서 정리해보겠다.

#include "csapp.h"

volatile sig_atomic_t pid;

void sigchld_handler(int s)
{
    int olderrno = errno;
    pid = Waitpid(-1, NULL, 0);
    errno = olderrno;
}

void sigint_handler(int s)
{
}

int main(int argc, char **argv)
{
    sigset_t mask, prev;
    Signal(SIGCHLD, sigchld_handler);
    Signal(SIGINT, sigint_handler);
    Sigemptyset(&mask);
    Sigaddset(&mask, SIGCHLD);

    while (1)
    {
        Sigprocmask(SIG_BLOCK, &mask, &prev);
        if (Fork() == 0)
            exit(0);

        pid = 0;
        while (!pid)
            sigsuspend(&prev);

        sigprocmask(SIG_SETMASK, &prev, NULL);
        printf(".");
    }

    exit(0);
}

간단히 코드의 목적에 대해 설명하자면, 원래 스핀 루프의 자원이 낭비적이니 중간에 pause()를 넣어 해결하려 하지만 이 상황이 race condition을 가지게 되기 때문에 이걸 해결하려고 sigsuspend를 사용한 코드이다. 아무튼 여기서 보면 pid를 핸들러에서 바꿔주는데 이 변수가 volatile이 아니라면 main에서는 메모리에서 새로 가져오지 않기 때문에 바뀐 것을 모른 채 계속 루프를 돌 수 있다. 멀티 쓰레드의 경우에서도  컴파일러가 스레드의 존재  여부를 모르기 때문에  변수 값이 변경되지 않았다면  매번 새롭게  메모리에서 값을 읽어오지 않는다. 따라서 여러 스레드가  공유하는 전역 변수라면 volatile로  선언해 주거나  명시적으로 lock을 잡아야 한다. 이렇게 레지스터를 재사용하지 않고 반드시 메모리를 참조할 경우 가시성이 보장된다고 한다. 한 쓰레드가 메모리에 쓴 내용을 다른 스레드에서 보인다는 것을 의미한다.

 

참고: https://dojang.io/mod/page/view.php?id=749 , https://luyin.tistory.com/102

 

 

'CS' 카테고리의 다른 글

프로시저  (0) 2021.10.02
전통적 동기화 문제와 데드락  (0) 2021.09.26
동시성 이슈  (0) 2021.09.25
Nonlocal jumps  (0) 2021.09.24
dup, dup2 , 파일 서술자 복제 함수  (0) 2021.09.17