A variable defined as volatile means that this variable may be unexpectedly changed, so that the compiler will not assume the value of this variable. Precisely, the optimizer must carefully re-read the value of this variable every time when using this variable, rather than using the backup stored in the register. The following are examples of volatile variables:
1) Hardware registers of parallel devices (for example, Status Registers)
2) Non-automatic variables that will be accessed in an interrupt service subroutine)
3) variables shared by several tasks in multi-threaded applications
People who cannot answer this question will not be hired. I think this is the most basic problem to distinguish between C programmers and embedded system programmers. Embedded System programmers often deal with hardware, interruptions, RTOS, and so on, all of which require volatile variables. If you do not know volatile content, it will lead to disasters.
If the subject correctly answers this question (well, I doubt if it will be the case), I will take a closer look and see if this guy is fully aware of the importance of volatile.
1) can a parameter be const or volatile? Explain why.
2) can a pointer be volatile? Explain why.
3) What are the following function errors:
Int square (volatile int * PTR)
{
Return * PTR ** PTR;
}
The answer is as follows:
1) Yes. One example is read-only status registers. It is volatile because it may be unexpectedly changed. It is const because the program should not try to modify it.
2) Yes. Although this is not very common. One example is when an interrupt service subroutine modifies a pointer to a buffer.
3) This code contains a prank. The purpose of this Code is to return the pointer * PTR points to the square of the value. However, since * PTR points to a volatile parameter, the compiler will generate code similar to the following:
Int square (volatile int * PTR)
{
Int A, B;
A = * PTR;
B = * PTR;
Return a * B;
}
* The value of * PTR may be unexpectedly changed, so a and B may be different. As a result, the returned value of this Code may not be the expected square value! The correct code is as follows:
Long square (volatile int * PTR)
{
Int;
A = * PTR;
Return a *;
}
========================================================== ==========================================================
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) dosomething ();
}
}
/* Interrupt service routine .*/
Void isr_2 (void)
{
I = 1;
}
The program is intended to call the dosomething 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, dosomething 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 memory-mapped hardware registers, because each read/write operation may have different meanings;
In addition, in the above situations, we often need to consider data integrity at the same time (the reading half of the correlated symbols is interrupted and overwritten). In 1, we can achieve this through related interruptions, task Scheduling can be disabled in 2, and 3 can only rely on the good design of hardware.
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 and other optimizations further eliminate 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. Volatile variables are not cached in registers 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 and Write volatile variables will not be optimized. If you assign a value to a variable but it is not used later, the compiler can often omit the value ***. However, the processing of memory mapped Io cannot be optimized in this way.
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.
========================================================== ==========================================================
The key lies in two aspects:
1. Compiler Optimization
In this thread, when reading a variable, in order to improve the access speed, the compiler sometimes first reads the variable to a register during optimization; later, when getting the variable value, the value is taken directly from the register. When the variable value changes in the local thread, the new value of the variable is copied to the register at the same time to maintain consistency; when the value of a variable changes due to other threads, the value of this register will not change accordingly, resulting in inconsistent values read by the application and the actual value of the variable; when the value of this register is changed due to other threads, the value of the original variable will not change, resulting in inconsistent values read by the application and the actual variable value.
Here is an inaccurate example:
When paying, the accountant calls the employee to register their bank card number every time. Once the accountant did not register immediately for convenience, he used the previously registered bank card number, an employee's bank card has been lost, and the bank card number has been reported, causing the employee to be unable to receive the salary.
Employee-original variable address
Bank Card number-backup of original variables in registers
2. Under what circumstances will this happen?
1) Hardware registers of parallel devices (for example, Status Registers)
2) Non-automatic variables that will be accessed in an interrupt service subroutine)
3) variables shared by several tasks in multi-threaded applications
Supplement: Volatile should be interpreted as "direct access to the original memory address", which is quite misleading;
"Easy to change" is caused by external factors, such as multithreading and interruptions. It is not because the variable modified by volatile is "easy to change". If there is no external cause, it is defined by volatile, it will not change either;
With the definition of volatile, this variable will not change because of external changes, so you can use it with confidence. Let's see if the previous explanation (Changeable) is misleading.
------------ Concise example ------------------
The volatile keyword is a type modifier that uses the type variables it declares to indicate that it can be changed by unknown factors of Some compilers, such as operating systems, hardware, or other threads. When the variable declared by this keyword is encountered, the compiler will not optimize the code that accesses the variable, so as to provide stable access to the special address.
An example of using this keyword is as follows:
Int volatile nvint;
>>>> When the value of a variable declared by volatile is required, the system always reads data from the memory where it is located, even if the previous command has just read data from it. The read data is saved immediately.
For example:
Volatile int I = 10;
Int A = I;
...
// Other code that does not explicitly tell the compiler to perform operations on I
Int B = I;
>>>> Volatile indicates that I may change at any time. Each time you use it, you must read it from the I address, therefore, the compilation code generated by the compiler will read data from the I address again and put it in B. The optimization method is that because the compiler finds that the code between the codes that read data from I has not performed any operations on I, it will automatically put the data that was last read in B. Instead of reading from I again. In this way, if I is a register variable or indicates that data on a port is prone to errors, volatile can ensure stable access to special addresses.
>>>> Note: In vc6, code optimization is not performed in the general debugging mode, so the function of this keyword cannot be seen. Insert the assembly code to test whether the volatile keyword exists and the effect on the final code of the program is as follows:
>>>> First, use classwizard to create a Win32 console project, insert a voltest. cpp file, and enter the following code:
>
# Include <stdio. h>
Void main ()
{
Int I = 10;
Int A = I;
Printf ("I = % d", );
// The purpose of the following Assembly statement is to change the I value in the memory, but it does not let the compiler know
_ ASM {
MoV dword ptr [ebp-4], 20 h
}
Int B = I;
Printf ("I = % d", B );
}
Then, run the program in debug version mode and the output result is as follows:
I = 10
I = 32
Then, run the program in release version mode. The output result is as follows:
I = 10
I = 10
The output results obviously show that in the release mode, the compiler optimizes the code and does not output the correct I value for the second time. Next, we add the volatile keyword to the I statement to see what changes have taken place:
# Include <stdio. h>
Void main ()
{
Volatile int I = 10;
Int A = I;
Printf ("I = % d", );
_ ASM {
MoV dword ptr [ebp-4], 20 h
}
Int B = I;
Printf ("I = % d", B );
}
Run the program in the debug version and release version respectively, and the output is:
I = 10
I = 32
This indicates that this keyword plays its role!
------------------------------------
Variables corresponding to volatile may change when your program does not know it. For example, multiple programs can manipulate this variable in the multi-threaded program and memory accessed together, your program cannot determine that the variable will change. For example, it corresponds to a certain state of an external device. When an external device operates, through the driver and interrupt events, the system changes the value of this variable, and your program does not know.
For volatile variables, the system extracts them directly from the corresponding memory every time it uses them, instead of using the original values in the cache, to adapt to its unknown changes, the system will not optimize the processing of such variables-obviously because its values may change at any time.
Bytes --------------------------------------------------------------------------------------
Typical Example:
For (INT I = 0; I <100000; I ++ );
This statement is used to test the speed of an empty loop.
But the compiler must optimize it and it will not be executed at all.
If you write:
For (volatile int I = 0; I <100000; I ++ );
It will execute