문제

무엇을 하는가? volatile 키워드는?C++에서는 어떤 문제가 해결되나요?

내 경우에는 고의로 필요한 적이 없었습니다.

도움이 되었습니까?

해결책

volatile 예를 들어 완전히 별도의 프로세스/장치/쓸 수 있는 메모리의 한 지점에서 읽는 경우 필요합니다.

나는 스트레이트 C의 다중 프로세서 시스템에서 듀얼 포트 램을 사용하여 작업했습니다.우리는 하드웨어 관리 16비트 값을 세마포어로 사용하여 다른 사람이 언제 완료되었는지 알 수 있었습니다.기본적으로 우리는 이렇게 했습니다:

void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

없이 volatile, 최적화 프로그램은 루프를 쓸모없는 것으로 간주합니다(그 사람은 값을 설정하지 않습니다!그는 미쳤습니다. 해당 코드를 제거하십시오!) 그리고 내 코드는 세마포어를 획득하지 않고 진행되어 나중에 문제를 일으킬 것입니다.

다른 팁

volatile 메모리 매핑된 하드웨어 장치를 읽거나 써야 하는 임베디드 시스템이나 장치 드라이버를 개발할 때 필요합니다.특정 장치 레지스터의 내용은 언제든지 변경될 수 있으므로 volatile 이러한 액세스가 컴파일러에 의해 최적화되지 않도록 보장하는 키워드입니다.

일부 프로세서에는 64비트 이상의 정밀도를 갖는 부동 소수점 레지스터가 있습니다(예:SSE가 없는 32비트 x86, Peter의 설명 참조).이렇게 하면 배정밀도 숫자에 대해 여러 작업을 실행하면 각 중간 결과를 64비트로 자르는 경우보다 실제로 더 높은 정밀도의 답을 얻을 수 있습니다.

이것은 일반적으로 훌륭하지만 컴파일러가 레지스터를 할당하고 최적화를 수행한 방법에 따라 정확히 동일한 입력에 대해 완전히 동일한 작업에 대해 다른 결과를 얻을 수 있음을 의미합니다.일관성이 필요한 경우 휘발성 키워드를 사용하여 각 작업을 강제로 메모리로 되돌릴 수 있습니다.

대수학적 의미는 없지만 Kahan 합계와 같이 부동 소수점 오류를 줄이는 일부 알고리즘에도 유용합니다.대수적으로는 잘못된 것이므로 일부 중간 변수가 휘발성이 아닌 이상 잘못 최적화되는 경우가 많습니다.

에서 "약속만큼 변덕스럽다" Dan Saks의 기사:

(...) 휘발성 개체는 값이 자발적으로 변경될 수 있는 개체입니다.즉, 개체를 휘발성으로 선언하면 프로그램에서 개체를 변경하는 명령문이 나타나지 않더라도 개체의 상태가 변경될 수 있음을 컴파일러에 알리는 것입니다."

다음은 그의 기사 3개에 대한 링크입니다. volatile 예어:

잠금 없는 데이터 구조를 구현할 때는 휘발성을 사용해야 합니다.그렇지 않으면 컴파일러는 변수에 대한 액세스를 자유롭게 최적화할 수 있으며 이로 인해 의미가 변경됩니다.

다르게 말하면, 휘발성은 이 변수에 대한 액세스가 물리적 메모리 읽기/쓰기 작업에 해당해야 함을 컴파일러에 알립니다.

예를 들어, Win32 API에서 InterlockedIncrement가 선언되는 방식은 다음과 같습니다.

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);

1990년대 초반에 내가 작업했던 대형 애플리케이션에는 setjmp와 longjmp를 사용한 C 기반 예외 처리 기능이 포함되어 있었습니다.휘발성 키워드는 해당 변수가 레지스터에 저장되고 longjmp에 의해 지워지지 않도록 "catch" 절 역할을 하는 코드 블록에 값을 보존해야 하는 변수에 필요했습니다.

Standard C에서 사용하는 곳 중 하나 volatile 신호 처리기가 있습니다.실제로 표준 C에서 신호 처리기에서 안전하게 수행할 수 있는 작업은 volatile sig_atomic_t 변수를 사용하거나 빠르게 종료하세요.실제로 AFAIK는 표준 C에서 다음을 사용하는 유일한 장소입니다. volatile 정의되지 않은 동작을 피하려면 필요합니다.

ISO/IEC 9899:2011 §7.14.1.1 signal 기능

¶5 호출 결과 이외의 신호가 발생하는 경우 abort 또는 raise 함수, 신호 핸들러가 정적 또는 스레드 스토리지 지속 시간이있는 객체를 언급하는 경우 동작이 정의되지 않습니다. volatile sig_atomic_t, 또는 신호 처리기는 표준 라이브러리에서 모든 기능을 호출합니다. abort 기능, _Exit 기능, quick_exit 기능 또는 signal 첫 번째 인수와 함께 핸들러의 호출을 일으킨 신호에 해당하는 신호 번호와 동일합니다.게다가 그런 전화가 오면 signal 함수는 SIG_ERR 리턴, 값을 초래합니다. errno 불확실하다.252)

252) 비동기 신호 처리기에 의해 신호가 생성되면 동작이 정의되지 않습니다.

즉, 표준 C에서는 다음과 같이 작성할 수 있습니다.

static volatile sig_atomic_t sig_num = 0;

static void sig_handler(int signum)
{
    signal(signum, sig_handler);
    sig_num = signum;
}

그리고 그다지 많지 않습니다.

POSIX는 신호 처리기에서 수행할 수 있는 작업에 대해 훨씬 관대하지만 여전히 제한 사항이 있습니다. 제한 사항 중 하나는 표준 I/O 라이브러리입니다. printf() et al — 안전하게 사용할 수 없습니다).

임베디드를 개발하면서 인터럽트 핸들러에서 변경될 수 있는 변수를 확인하는 루프가 있습니다."휘발성"이 없으면 루프는 멍청한 짓이 됩니다. 컴파일러가 알 수 있는 한 변수는 절대 변경되지 않으므로 검사를 최적화합니다.

보다 전통적인 환경에서는 다른 스레드에서 변경될 수 있는 변수에도 동일한 내용이 적용되지만 여기서는 종종 동기화 호출을 수행하므로 컴파일러는 최적화에 그다지 자유롭지 않습니다.

컴파일러가 코드를 단계별로 볼 때 볼 수 있는 변수를 최적화해야 한다고 주장할 때 디버그 빌드에서 이를 사용했습니다.

의도한 대로 사용하는 것 외에도 휘발성은 (템플릿) 메타 프로그래밍에 사용됩니다.휘발성 속성(예: const)이 오버로드 해결에 참여하므로 실수로 인한 오버로드를 방지하는 데 사용할 수 있습니다.

template <typename T> 
class Foo {
  std::enable_if_t<sizeof(T)==4, void> f(T& t) 
  { std::cout << 1 << t; }
  void f(T volatile& t) 
  { std::cout << 2 << const_cast<T&>(t); }

  void bar() { T t; f(t); }
};

이는 합법적입니다.두 오버로드는 모두 잠재적으로 호출 가능하며 거의 동일합니다.출연진은 volatile 바가 비휘발성을 통과하지 않는다는 것을 알고 있으므로 과부하는 합법적입니다. T 그래도.그만큼 volatile 그러나 버전은 엄격하게 더 나쁩니다. 따라서 비휘발성 버전인 경우 과부하 해결에서 선택하지 마십시오. f 사용할 수 있습니다.

코드는 실제로 다음에 의존하지 않습니다. volatile 메모리 접근.

  1. 스핀락과 일부 (모두?) 잠금 없는 데이터 구조를 구현하려면 이를 사용해야 합니다.
  2. 원자적 연산/명령과 함께 사용
  3. 컴파일러 버그(최적화 중에 잘못 생성된 코드)를 극복하는 데 도움이 되었습니다.

그만큼 volatile 키워드는 컴파일러가 결정할 수 없는 방식으로 변경될 수 있는 객체에 대해 컴파일러가 최적화를 적용하지 못하도록 방지하기 위한 것입니다.

다음과 같이 선언된 객체 volatile 해당 값은 언제든지 현재 코드 범위 밖의 코드에 의해 변경될 수 있으므로 최적화에서 생략됩니다.시스템은 항상 현재 값을 읽습니다. volatile 이전 명령이 동일한 객체의 값을 요청한 경우에도 요청된 시점의 임시 레지스터에 해당 값을 유지하지 않고 메모리 위치에서 객체를 가져옵니다.

다음과 같은 경우를 고려하십시오

1) 범위 밖의 인터럽트 서비스 루틴에 의해 수정된 전역 변수.

2) 멀티스레드 애플리케이션 내의 전역 변수.

휘발성 한정자를 사용하지 않으면 다음과 같은 문제가 발생할 수 있습니다.

1) 최적화가 활성화된 경우 코드가 예상대로 작동하지 않을 수 있습니다.

2) 인터럽트가 활성화되어 사용될 때 코드가 예상대로 작동하지 않을 수 있습니다.

휘발성 물질:프로그래머의 가장 친한 친구

https://en.wikipedia.org/wiki/Volatile_(컴퓨터_프로그래밍)

휘발성 키워드는 컴파일러에게 일부 변수(스레드나 인터럽트 루틴에 의해 수정될 수 있음)에 대한 액세스를 최적화하지 말라고 지시하는 데 사용된다는 사실 외에도 일부 컴파일러 버그를 제거하는 데 사용됨 -- 네 그럴 수 있어요 ---.

예를 들어 저는 임베디드 플랫폼에서 작업했는데 컴파일러가 변수 값과 관련하여 잘못된 가정을 하고 있었습니다.코드가 최적화되지 않으면 프로그램이 정상적으로 실행됩니다.최적화(중요한 루틴이기 때문에 실제로 필요함)를 사용하면 코드가 올바르게 작동하지 않습니다.(매우 정확하지는 않지만) 유일한 해결책은 '결함이 있는' 변수를 휘발성으로 선언하는 것이었습니다.

귀하의 프로그램은 없이도 작동하는 것 같습니다. volatile 예어?아마도 이것이 이유일 것이다:

앞서 언급했듯이 volatile 키워드는 다음과 같은 경우에 도움이 됩니다.

volatile int* p = ...;  // point to some memory
while( *p!=0 ) {}  // loop until the memory becomes zero

하지만 외부 또는 인라인이 아닌 함수가 호출되면 효과가 거의 없는 것 같습니다.예:

while( *p!=0 ) { g(); }

그러면 유무에 관계없이 volatile 거의 동일한 결과가 생성됩니다.

g()가 완전히 인라인될 수 있는 한 컴파일러는 진행 중인 모든 것을 볼 수 있으므로 최적화할 수 있습니다.그러나 프로그램이 컴파일러가 무슨 일이 일어나고 있는지 볼 수 없는 위치를 호출할 때 컴파일러가 더 이상 가정을 하는 것은 안전하지 않습니다.따라서 컴파일러는 항상 메모리에서 직접 읽는 코드를 생성합니다.

그러나 함수 g()가 인라인이 될 때(명시적인 변경으로 인해 또는 컴파일러/링커의 영리함으로 인해) 잊어버린 경우 코드가 깨질 수 있습니다. volatile 예어!

그러므로 나는 volatile 프로그램이 없어도 작동하는 것처럼 보이더라도 키워드입니다.이는 향후 변경 사항에 대한 의도를 더욱 명확하고 강력하게 만듭니다.

C 초기에 컴파일러는 lvalue를 읽고 쓰는 모든 작업을 메모리 작업으로 해석하여 코드에 나타나는 읽기 및 쓰기와 동일한 순서로 수행되었습니다.컴파일러에게 작업 순서를 변경하고 통합할 수 있는 자유가 어느 정도 부여된다면 많은 경우 효율성이 크게 향상될 수 있지만 여기에는 문제가 있었습니다.작업조차도 특정 순서로 지정하는 경우가 많았습니다. 일부 그래서 프로그래머는 똑같이 좋은 여러 대안 중 하나를 선택했지만 항상 그런 것은 아니었습니다.때로는 특정 작업이 특정 순서로 발생하는 것이 중요할 수 있습니다.

정확히 어떤 시퀀싱 세부 사항이 중요한지는 대상 플랫폼과 응용 분야에 따라 달라집니다.특히 세부적인 제어를 제공하는 대신 표준은 간단한 모델을 선택했습니다.정규화되지 않은 lvalue를 사용하여 일련의 액세스가 수행되는 경우 volatile, 컴파일러는 적절하다고 판단되는 대로 순서를 변경하고 통합할 수 있습니다.작업이 완료되면 volatile-Qualified lvalue의 경우, 품질 구현은 비표준 구문을 사용할 필요 없이 의도한 플랫폼 및 애플리케이션 필드를 대상으로 하는 코드에 필요할 수 있는 추가 순서 보장을 제공해야 합니다.

불행하게도 많은 컴파일러는 프로그래머에게 필요한 보장이 무엇인지 파악하기보다는 표준에서 요구하는 최소한의 보장만 제공하는 쪽을 선택했습니다.이것은 만든다 volatile 있어야 할 것보다 훨씬 덜 유용합니다.예를 들어, gcc 또는 clang에서 기본적인 "핸드오프 뮤텍스"(뮤텍스를 획득하고 해제한 작업은 다른 작업이 완료할 때까지 다시 수행하지 않음)를 구현해야 하는 프로그래머는 다음 중 하나를 수행해야 합니다. 네 가지 중:

  1. 컴파일러가 인라인할 수 없고 전체 프로그램 최적화를 적용할 수 없는 함수에 뮤텍스의 획득 및 해제를 넣습니다.

  2. 뮤텍스에 의해 보호되는 모든 객체를 다음과 같이 규정합니다. volatile--뮤텍스를 획득한 후 해제하기 전에 모든 액세스가 발생하는 경우 필요하지 않은 것입니다.

  3. 최적화 수준 0을 사용하여 컴파일러가 정규화되지 않은 모든 개체인 것처럼 코드를 생성하도록 합니다. register ~이다 volatile.

  4. gcc 관련 지시문을 사용하십시오.

대조적으로, icc와 같은 시스템 프로그래밍에 더 적합한 고품질 컴파일러를 사용하는 경우 다른 옵션이 있습니다.

  1. 다음을 확인하세요. volatile-적격한 쓰기는 획득 또는 릴리스가 필요한 모든 곳에서 수행됩니다.

기본적인 "핸드오프 뮤텍스"를 획득하려면 다음이 필요합니다. volatile 읽고(준비가 되었는지 확인하기 위해), volatile (상대방은 돌려줄 때까지 다시 획득하려고 시도하지 않음) 의미 없는 작업을 수행해야 합니다. volatile write는 gcc나 clang에서 사용할 수 있는 옵션보다 여전히 좋습니다.

제가 상기시켜 드리고 싶은 한 가지 용도는 신호 처리기 함수에서 전역 변수에 액세스/수정하려는 경우(예: 종료 = true로 표시) 해당 변수를 '휘발성'으로 선언해야 한다는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top