Detailed description of volatile keywords in C

Source: Internet
Author: User

Volatile reminds the compiler that the variables defined after it may change at any time. Therefore, the compiled program will directly read data from the variable address every time it needs to store or read the variable. If there is no volatile keyword, the compiler may optimize reading and storage, and may temporarily use the value in the register. If this variable is updated by another program, it will be inconsistent. The following is an example. In DSP development, it is often necessary to wait for an event to be triggered, so such a program is often written:
Short flag;
Void test ()
{
Do1 ();
While (flag = 0 );
Do2 ();
}
This program will run do2 () only after the flag value of the memory variable changes to 1 (it is suspected that it is 0 (). The flag variable value is changed by another program, which may be a hardware interrupt service program. For example, if a button is pressed, the DSP is interrupted and the flag is changed to 1 in the key interrupt program so that the above program can continue to run. However, the compiler does not know that the flag value will be modified by other programs. Therefore, when it is optimized, it may first read the flag value into a register, and then wait until that register changes to 1. If this optimization is unfortunate, the while loop becomes an endless loop, because the contents of the register cannot be modified by the interrupted service program. To allow the program to read the value of the true flag variable every time, it must be defined as follows:
Volatile short flag;
It should be noted that it may run normally without volatile, but it may not run properly after the compiler optimization level is modified. Therefore, the debug version is normal, but the release version is not normal. For security, the volatile keyword is added as long as you wait for another program to modify a variable.

Volatile is intended to be "changeable"
Since the speed of accessing registers is faster than that of RAM, the compiler generally reduces the access to external RAM. For example:
Static int I = 0;
Int main (void)
{
...
While (1)
{
If (I) do_something ();
}
}
/* Interrupt service routine .*/
Void ISR_2 (void)
{
I = 1;
}
The program is intended to call the do_something function in main when ISR_2 is interrupted. However, since the compiler judges that I has not been modified in the main function, therefore, you may only perform one read operation from I to a register, and then use only the "I copy" in the register for each if judgment. As a result, do_something will never be called. If the variable is modified with volatile, the compiler ensures that the read and write operations on the variable are not optimized (certainly executed ). In this example, I should also describe this.
Generally, volatile is used in the following areas:
1. volatile must be added to the variable modified in the interrupted service program for testing by other programs;
2. volatile should be added to the labels shared by all tasks in a multi-task environment;
3. volatile is also required for the hardware registers mapped to memory, because each read/write to it may have different meanings;
In addition, in the above situations, we often need to consider data integrity at the same time (some of the correlated symbols have been read in half and are interrupted for rewriting). In 1, we can achieve this through Guanzhong disconnection, task Scheduling can be disabled in 2, and 3 can only rely on the good design of hardware.
Ii. Meaning of volatile
Volatile is always related to optimization. the compiler has a technology called data stream analysis, which analyzes where variables are assigned, where they are used, and where they are invalid. The analysis results can be used for constant merging, constant propagation optimization, further eliminating code. However, sometimes these optimizations are not required by the program. In this case, you can use the volatile keyword to disable these optimizations. the literal meaning of volatile is variable and has the following functions:
1. The volatile variable is not cached in the register between two operations. In a multi-task, interrupted, or even setjmp environment, variables may be changed by other programs, and the compiler itself cannot know. volatile tells the compiler this situation.
2. Do not optimize constant merging or constant propagation, so it is like the following code:
Volatile int I = 1;
If (I> 0 )...
If conditions are not considered unconditional.
3. read/write of volatile variables will not be optimized. If you assign values to a variable but do not use them later, the compiler can often omit the assignment operation. However, the processing of Memory Mapped IO cannot be optimized like this.
Some people mentioned above that volatile can ensure the atomicity of memory operations. This is not accurate. First, x86 requires a LOCK prefix to ensure atomicity under SMP. Second, some other methods, such as atomic_inc, must be used to guarantee atomicity.
For jiffies, it has been declared as a volatile variable. I think it is enough to use jiffies ++ directly. There is no need to use that complex form, because it cannot guarantee atomicity.
You may not know the following two sets of commands in Pentium and subsequent CPUs:
Inc jiffies
;;
Mov jiffies, % eax
Inc % eax
Mov % eax, jiffies
It works the same, but one command is not as fast as three commands.
Iii. Compiler Optimization → C keyword volatile → memory destroy descriptor zz
"Memory" is special and may be the most difficult part of Embedded Assembly. To explain it clearly, first introduce the compiler optimization knowledge, and then look at the C keyword volatile. Finally, let's look at the descriptor.
1. Introduction to Compiler Optimization
The memory access speed is far less than the CPU processing speed. To improve the overall performance of the machine, hardware high-speed Cache is introduced on the hardware to accelerate memory access. In addition, commands in the modern CPU are not necessarily executed in strict order. commands without relevance can be executed in disorder to make full use of the CPU command line and improve the execution speed. The above is the hardware-level optimization. Let's look at optimization at the software level: one is optimized by the programmer when writing the code, and the other is optimized by the compiler. Common Methods for Compiler optimization are: caching memory variables to registers; adjusting the command order to make full use of the CPU command line; common is re-sorting read/write commands. When optimizing regular memory, these optimizations are transparent and efficient. The solution to the problem caused by Compiler Optimization or hardware re-sorting is from the hardware (or other processors) the memory barrier (memory barrier) must be set between operations executed in a specific sequence. linux provides a macro to solve the execution sequence problem of the compiler.
Void Barrier (void)
This function notifies the compiler to insert a memory barrier, but it does not work for the hardware. The compiled code will store all the modified values in the current CPU Register into the memory, read the data from the memory again when necessary.
2. C language keyword volatile
The C language keyword volatile (note that it is used to modify the variable rather than the _ volatile _ mentioned above) indicates that the value of a variable may be changed externally, therefore, the access to these variables cannot be cached in registers and must be re-accessed each time. This keyword is often used in a multi-threaded environment, because the same variable may be modified by multiple threads when a multi-threaded program is compiled, and the program synchronizes various threads through this variable. For example:
DWORD _ stdcall threadFunc (LPVOID signal)
{
Int * intSignal = reinterpret_cast <int *> (signal );
* IntSignal = 2;
While (* intSignal! = 1)
Sleep (1000 );
Return 0;
}
Set intSignal to 2 when the thread starts, and then wait until it exits when intSignal is 1. Obviously, the intSignal value must be changed externally; otherwise, the thread will not exit. However, the thread does not exit during actual operation. Even if the value of the thread is changed to 1 in the external department, you can see the corresponding pseudo assembly code:
Mov ax, signal
Label:
If (ax! = 1)
Goto label
For the C compiler, it does not know that this value will be modified by other threads. Naturally, it is cached in the register. Remember, the C compiler has no thread concept! Volatile is required. Volatile indicates that the value may be changed outside the current thread. That is to say, we need to add the volatile keyword before intSignal in threadFunc. At this time, the compiler knows that the value of this variable will change externally, so it will read the variable again every time it is accessed, the following pseudo code shows how the loop is made:
Label:
Mov ax, signal
If (ax! = 1)
Goto label
3. Memory
With the above knowledge, it is not difficult to understand the Memory modification descriptor. The Memory descriptor tells GCC:
1) do not re-sort the assembly instructions embedded in this section with the preceding commands. That is, before the embedded assembly code is executed, all the preceding commands are executed.
2) Do not cache variables in registers, because this Code may use memory variables which may change in unpredictable ways, therefore, GCC inserts necessary code to first write the variable values cached in the register back to the memory. If you access these variables later, you need to re-access the memory.
If the Assembly command modifies the memory, but the GCC itself does not notice it, because there is no description in the output part. In this case, you need to add "memory" in the description section to tell GCC that the memory has been modified, after GCC learns this information, it will insert the necessary commands before this command to write the variable values in the Cache to the register back to the memory, if you want to use these variables again later, read them again.
You can also use "volatile" to achieve this goal. However, it is better to use "memory" to add this keyword before each variable.



From the trace

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.