C에 휘발성이 필요한 이유는 무엇입니까?
왜?volatile
C에 필요한가요?어디에 쓰는 거죠?어떻게 될까요?
volatile
컴파일러에게, 에 관련하는 어떠한 것도 최적화하지 말아 주세요.volatile
변수.
이를 사용하는 일반적인 이유는 적어도3가지입니다.모두 가시적인 코드에서 액션 없이 변수 값이 변경될 수 있는 상황입니다.값 자체를 변경하는 하드웨어와의 인터페이스, 변수를 사용하는 다른 스레드가 실행되고 있는 경우 또는 변수의 값을 변경할 수 있는 신호 핸들러가 있는 경우.
RAM에 매핑되어 있고 명령 포트와 데이터 포트라는2개의 주소를 가진 하드웨어가 있다고 가정합니다.
typedef struct
{
int command;
int data;
int isBusy;
} MyHardwareGadget;
이제 몇 가지 명령을 전송하려고 합니다.
void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isbusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
간단해 보이지만 컴파일러가 데이터와 명령어를 쓰는 순서를 자유롭게 변경할 수 있기 때문에 실패할 수 있습니다.이로 인해 우리의 작은 가젯이 이전 데이터 값으로 명령을 발행하게 됩니다.또, 비지 루프중의 대기도 봐 주세요.그것은 최적화됩니다.컴파일러는 영리해지려고 할 것입니다.isBusy
무한 루프에 빠질 수 있습니다.네가 원하는 건 그게 아니야
이 문제를 해결하는 방법은 포인터를 선언하는 것입니다.gadget
~하듯이volatile
이렇게 하면 컴파일러는 당신이 쓴 것을 할 수 밖에 없습니다.메모리 할당을 제거할 수도 없고 레지스터에 변수를 캐시할 수도 없으며 할당 순서를 변경할 수도 없습니다.
올바른 버전입니다.
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isBusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
volatile
C는 변수의 값을 자동으로 캐싱하지 않기 위해 실제로 존재하게 되었습니다.컴파일러에 이 변수의 값을 캐시하지 않도록 지시합니다.따라서 주어진 값을 얻기 위한 코드를 생성합니다.volatile
메인 메모리와 마주칠 때마다 변수입니다.이 메커니즘은 OS 또는 인터럽트에 의해 언제든지 값을 변경할 수 있기 때문에 사용됩니다.그래서 사용하다volatile
매번 새로운 가치에 접근할 수 있도록 도와드립니다.
다른 용도:volatile
는 시그널 핸들러입니다.다음과 같은 코드가 있는 경우:
int quit = 0;
while (!quit)
{
/* very small loop which is completely visible to the compiler */
}
컴파일러는 루프 본체가 이 루프 본체에 닿지 않는 것을 알 수 있습니다.quit
루프를 변수로 변환합니다.while (true)
루프. 심지어 그 루프가quit
시그널 핸들러에 변수가 설정되어 있다.SIGINT
그리고.SIGTERM
컴파일러는 그것을 알 방법이 없습니다.
다만,quit
변수가 선언되었습니다.volatile
컴파일러는 다른 곳에서 변경할 수 있기 때문에 매번 로드하도록 강제됩니다.이게 바로 이 상황에서 네가 원하는 거야.
volatile
는 컴파일러에 액세스하고 있는 코드가 아닌 다른 방법으로 변수를 변경할 수 있음을 알려줍니다.예를 들어 I/O 매핑된 메모리 위치일 수 있습니다.이러한 경우에 특정되지 않으면 일부 가변 액세스를 최적화할 수 있다.예를 들어, 그 내용은 레지스터에 보관 유지되고 메모리 위치는 다시 읽어들이지 않는다.
간단한 설명은 다음과 같습니다.
일부 시나리오에서는 논리 또는 코드에 기초하여 컴파일러는 변경되지 않는다고 생각하는 변수의 최적화를 수행합니다.volatile
키워드를 지정하면 변수가 최적화되지 않습니다.
예를 들어 다음과 같습니다.
bool usb_interface_flag = 0;
while(usb_interface_flag == 0)
{
// execute logic for the scenario where the USB isn't connected
}
는 「이러다」라고 생각할 수 .usb_interface_flag
0 0 로 0 、 while loop 、 이 0 이이 。후 를 "Drughta"로 합니다.while(true)
그 결과 무한 루프가 발생합니다.
이러한 시나리오를 피하기 위해 플래그를 휘발성으로 선언합니다.이 값은 외부 인터페이스나 프로그램의 다른 모듈에 의해 변경될 수 있습니다.즉, 최적화하지 말아 주세요.휘발성의 사용 사례입니다.
Dennis Ritchie가 설계한 언어에서는 주소가 사용되지 않은 자동 객체를 제외한 모든 객체에 대한 액세스는 객체의 주소를 계산한 후 해당 주소의 스토리지를 읽거나 쓰는 것처럼 동작합니다.이로 인해 언어는 매우 강력했지만 최적화 기회는 심각하게 제한되었습니다.
컴파일러가 특정 오브젝트가 이상한 방법으로 변경되지 않는다고 가정하는 수식자를 추가하는 것은 가능하지만, 이러한 가정은 C 프로그램의 대부분의 오브젝트에 적합하며, 그러한 가정이 적용되는 모든 오브젝트에 수식자를 추가하는 것은 실용적이지 않습니다. 일부 에서는 그러한 되지 않는 .한편, 일부 프로그램에서는 그러한 가정이 성립하지 않는 오브젝트를 사용해야 합니다. 이 에서는 컴파일러가 가 컴파일러로 될 수 .volatile
컴파일러의 관리 범위를 벗어나는 방법으로 값이 관찰되거나 변경되지 않거나 컴파일러가 이해할 수 없는 경우.
컴파일러가 수 이 다를 수 는 컴파일러의 .volatile
으로 한 가 처리되어야 한다고 하지 못했기 에 이 컴파일러는 처리되지 않았습니다.volatile
플랫폼에서의 특정 읽기/쓰기 조작의 모든 관련 효과를 인식할 수 있는 방법으로 많은 컴파일러는 백그라운드 I/O 등의 처리를 효율적이면서도 컴파일러의 "최적화"에 의해 중단되지 않는 방법으로 처리하기가 어려워집니다.
Andrei Alexandrescu의 이 기사 "volatile - Multithreaded Programmer's Best Friend"를 참조하십시오.
volatile 키워드는 특정 비동기 이벤트가 존재하는 경우 코드를 잘못 표시할 수 있는 컴파일러 최적화를 방지하기 위해 고안되었습니다.예를 들어, 원시 변수를 휘발성으로 선언하면 컴파일러는 레지스터에 캐시할 수 없습니다.이것은 변수가 여러 스레드 간에 공유될 경우 치명적인 일반적인 최적화입니다.따라서 일반적으로 여러 스레드 간에 공유해야 하는 원시 유형의 변수가 있는 경우 해당 변수를 휘발성으로 선언해야 합니다.그러나 이 키워드를 사용하면 실제로 더 많은 작업을 수행할 수 있습니다. 스레드 세이프가 아닌 코드를 캐치하기 위해 이 키워드를 사용할 수 있으며 컴파일 시 이를 수행할 수 있습니다.이 문서에서는 이 방법을 보여 줍니다.이 솔루션에는 코드의 중요한 섹션을 쉽게 시리얼화할 수 있는 간단한 스마트 포인터가 포함되어 있습니다.
은 두 가지 됩니다.C
★★★★★★★★★★★★★★★★★」C++
.
Scott Meyers와 Andrei Alexandrescu의 기사 "C++와 이중 체크 잠금의 위험"도 참조하십시오.
따라서 일부 메모리 위치(예: 메모리 매핑 포트 또는 ISR [Interrupt Service Routes]에서 참조되는 메모리)를 처리할 때 일부 최적화를 일시 중단해야 합니다.휘발성은 이러한 장소에 대한 특별한 처리를 지정하기 위해 존재한다. (1) 휘발성 변수의 내용은 "변화 가능"(컴파일러에 알려지지 않은 수단으로 변경될 수 있음), (2) 휘발성 데이터에 대한 모든 쓰기는 "변화 가능"하므로 그것들은 종교적으로 실행되어야 하며 (3) 휘발성 데이터에 대한 모든 연산은 t의 순서로 실행되어야 한다.hey는 소스 코드에 표시됩니다.처음 두 규칙은 적절한 읽기와 쓰기를 보장합니다.마지막으로 입력과 출력을 혼합한 I/O 프로토콜을 구현할 수 있습니다.이것은 비공식적으로 C와 C++의 휘발성이 보증하는 것입니다.
간단히 말해, 컴파일러는 특정 변수에 대해 어떠한 최적화도 수행하지 않도록 지시합니다.디바이스 레지스터에 매핑된 변수는 디바이스에 의해 간접적으로 변경됩니다.이 경우 휘발성을 사용해야 합니다.
에는 너무 것을 .volatile
예를 들면, Nils Pipenbrinck의 높은 투표율의 회답의 예를 봐 주세요.
그의 예는 적절하지 않다고 말할 수 있다.volatile
volatile
컴파일러가 유용하고 바람직한 최적화를 하지 못하도록 하는 데만 사용됩니다.스레드 세이프, 아토믹액세스, 메모리 순서등의 문제는 없습니다.
이 예에서는 다음과 같습니다.
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isbusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
그gadget->data = data
전에gadget->command = command
컴파일러에 의한 컴파일된 코드에서만 보증됩니다.실행 시 프로세서는 프로세서 아키텍처에 관한 데이터 및 명령어 할당 순서를 변경할 수 있습니다.하드웨어가 잘못된 데이터를 가져올 수 있습니다(가젯이 하드웨어 I/O에 매핑되어 있는 경우).데이터와 명령어 할당 사이에 메모리 장벽이 필요합니다.
두 가지 용도가 있습니다.이것들은 임베디드 개발에서 특히 많이 사용됩니다.
컴파일러는 volatile 키워드로 정의된 변수를 사용하는 함수를 최적화하지 않습니다.
휘발성은 RAM, ROM 등의 정확한 메모리 위치에 액세스하기 위해 사용됩니다.메모리 매핑 디바이스를 제어하고 CPU 레지스터에 액세스하여 특정 메모리 위치를 찾기 위해 더 자주 사용됩니다.
어셈블리 목록의 예를 참조하십시오.참조: 임베디드 개발에서의 C "volatile" 키워드 사용
휘발성의 한계 용도는 다음과 같습니다.함수의 숫자 도함수를 계산하려고 합니다.f
:
double der_f(double x)
{
static const double h = 1e-3;
return (f(x + h) - f(x)) / h;
}
문제는 말이다x+h-x
일반적으로 와 동등하지 않다h
반올림 오류로 인해.생각해 보세요: 매우 가까운 숫자를 추출하면, 많은 유효 자릿수가 손실되어 도함수의 연산을 방해할 수 있습니다(1.00001 - 1).생각할 수 있는 회피책은 다음과 같습니다.
double der_f2(double x)
{
static const double h = 1e-3;
double hh = x + h - x;
return (f(x + hh) - f(x)) / hh;
}
그러나 플랫폼과 컴파일러 스위치에 따라서는 그 함수의 두 번째 행이 적극적으로 최적화된 컴파일러에 의해 지워질 수 있습니다.그래서 네가 대신 써.
volatile double hh = x + h;
hh -= x;
컴파일러가 hh를 포함한 메모리 위치를 읽도록 강제하여 최종적인 최적화 기회를 상실합니다.
volatile은 스토리지가 언제든지 변경되고 변경될 가능성이 높지만 사용자 프로그램의 통제 밖에 있는 것을 의미합니다.즉, 변수를 참조할 경우 프로그램은 항상 물리 주소(매핑된 입력 fifo)를 확인하고 캐시 방식으로 사용하지 않아야 합니다.
휘발성이 중요한 또 다른 시나리오를 말씀드리겠습니다.
고속 I/O를 위해 파일을 메모리 매핑하고 그 파일이 백그라운드에서 변경될 수 있다고 가정합니다(예를 들어 파일이 로컬 하드 드라이브에 있는 것이 아니라 다른 컴퓨터에서 네트워크를 통해 제공됩니다).
메모리 매핑된 파일의 데이터에 (소스 코드 수준에서) 비휘발성 객체에 대한 포인터를 통해 액세스하면 컴파일러에 의해 생성된 코드가 사용자가 알지 못하는 사이에 동일한 데이터를 여러 번 가져올 수 있습니다.
데이터가 변경되면 프로그램이 두 개 이상의 다른 버전의 데이터를 사용하게 되어 일관성 없는 상태가 될 수 있습니다.이로 인해 신뢰할 수 없는 파일 또는 신뢰할 수 없는 위치에서 파일을 처리할 경우 프로그램의 논리적으로 잘못된 동작뿐만 아니라 프로그램의 보안 취약점이 발생할 수 있습니다.
보안에 관심이 있고 그래야 하는 경우 이는 고려해야 할 중요한 시나리오입니다.
휘발성은 컴파일된 코드 외부에서 변경할 수 있습니다(예를 들어 프로그램은 휘발성 변수를 메모리 매핑 레지스터에 매핑할 수 있습니다).컴파일러는 휘발성 변수를 처리하는 코드에 특정 최적화를 적용하지 않습니다.예를 들어, 메모리에 쓰지 않고는 레지스터에 로드하지 않습니다.이는 하드웨어 레지스터를 다룰 때 중요합니다.
Wiki에서 모든 것을 언급하고 있습니다.volatile
:
또한 Linux 커널의 문서에는 다음과 같은 기법이 있습니다.volatile
:
휘발성은 컴파일러가 특정 코드 시퀀스를 최적화하지 않도록 강제하는 경우에도 유용합니다(예를 들어 마이크로 벤치마크 작성).
이것은 컴파일러가 변수의 값을 자동으로 변경하는 것을 허용하지 않습니다. 휘발성 변수는 동적 사용을 위한 것입니다.
여기서 많은 사람들이 올바르게 제안했듯이, volatile 키워드의 일반적인 용도는 volatile 변수의 최적화를 건너뛰는 것입니다.
생각나는 최고의 장점은 휘발성에 대해 읽은 후에 언급할 가치가 있는 것입니다. - 이 경우 변수의 롤백을 방지하는 것입니다.longjmp
. 로컬이 아닌 점프.
이것은 무엇을 의미합니까?
이는 단순히 스택 언바인딩을 실행한 후 마지막 값이 유지되고 이전 스택프레임으로 돌아간다는 것을 의미합니다.일반적으로 오류가 발생했을 경우입니다.
이 질문의 범위를 벗어나기 때문에 자세한 내용은 설명하지 않겠습니다.setjmp/longjmp
여기서 읽어보실 가치가 있습니다.또한 변동성 기능을 사용하여 마지막 값을 유지하는 방법도 있습니다.
언급URL : https://stackoverflow.com/questions/246127/why-is-volatile-needed-in-c
'programing' 카테고리의 다른 글
Vuejs 2: 스크립트 태그 javascript 파일을 DOM에 동적으로 포함(및 실행)하려면 어떻게 해야 합니까? (0) | 2022.07.18 |
---|---|
Objective-C에서 ENUM을 정의하고 사용하려면 어떻게 해야 합니까? (0) | 2022.07.11 |
Java에서 final 키워드를 사용하면 퍼포먼스가 향상됩니까? (0) | 2022.07.11 |
Vue.js - Javascript Dynamic Imports를 사용하여 다른 서버에서 컴포넌트를 로드할 수 있습니까? (0) | 2022.07.11 |
Vuex 프로파일링 : 메모리 내의 vuex 상태 또는 컴포넌트 크기를 확인하는 방법 (0) | 2022.07.11 |