프로그래밍임베디드/IoT 개발자

C 언어에서 volatile 키워드를 사용할 때의 세부 사항과 특히 멀티스레딩 및 하드웨어 레지스터 작업 시 잘못된 사용에 대한 오류는 무엇입니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

volatile 키워드는 컴파일러에게 변수가 컴파일러의 예상과 다르게 변경될 수 있음을 알려주며(예: 하드웨어, 다른 스레드 또는 인터럽트 핸들러에 의해), 그 값을 캐시하거나 접근을 최적화하지 않도록 합니다.

사용되는 경우:

  • 하드웨어 레지스터 작업 시.
  • 인터럽트 핸들러에서 변경되는 변수에 대해.
  • 스레드 간 상호작용에 사용되는 변수에 대해 (하지만 중요: volatile은 원자성이나 동기화를 보장하지 않습니다!).

사용 예:

volatile int flag = 0; void handler() { flag = 1; // 인터럽트 핸들러 } void loop() { while (!flag) { // 이벤트 대기 } // ... }

volatile 없이 컴파일러는 루프를 무한 루프로 대체할 수 있었지만, volatile을 사용하면 변수는 매번 메모리에서 읽혀집니다.


트릭 질문.

스레드 간의 정보 교환을 올바르게 수행하기 위해 volatile을 사용하는 것만으로 충분합니까?

자주 발생하는 오류는 volatile이 스레드 간의 메모리 동기화를 보장하고 원자성을 부여한다고 생각하는 것입니다.

정답:

volatile은 멀티스레딩 환경에서 데이터 경쟁으로부터 보호하지 않으며 메모리 장벽을 보장하지 않습니다: 이는 컴파일러에게 접근을 최적화하지 말라고 지시할 뿐입니다. 보장된 정확성을 위해 반드시 동기화 원시(예: mutex, atomic 작업 등)를 사용해야 합니다.

// 안전하지 않습니다! volatile int ready = 0; void thread1() { data = 123; // 데이터 기록 ready = 1; // 다른 스레드에 신호 } void thread2() { while (!ready); // 이벤트 대기 printf("data = %d\n", data); // 데이터가 아직 업데이트되지 않았을 수 있습니다! }

역사


마이크로컨트롤러 관리 프로젝트에서 메인 루프와 인터럽트 핸들러 간의 교환 변수이 volatile로 정의되지 않은 경우 펌웨어가 때때로 멈췄습니다 — 플래그 변경이 감지되지 않았습니다.


멀티스레드 서버에서는 신호 교환을 위해 volatile을 사용하여 충분하다고 생각했지만, 결국 어려운 버그를 발견하게 되었습니다: 때때로 스레드가 관련 없는 데이터를 읽거나 완전히 불일치된 상태로 읽었습니다 — 원자 변수나 mutex가 필요했습니다.


하드웨어 레지스터 작업 시 오류: 값을 volatile 없이 쓰거나 읽으며, 컴파일러가 접근을 최적화함에 따라 레지스터의 새로운 값을 완전히 무시하게 되었고 — 일부 명령이 작동하지 않았습니다.