# 重要的编程概念（即使在嵌入式系统上）部分III：波动率

2014年10月10日

1vol·a·tile adjective \ˈvä-lə-tə我，特别是英国 - ˌtī(-ə)l\
：可能会以非常突然或极端的方式改变
：有或表现出极端或突然的情感变化
：可能会变得危险或失控

## 编程语言支持波动性

In the immutability article, I talked about the const keyword in C/C++ and the final keyword in Java. Both languages have a volatile keyword, which I’请描述，但我对此做了一些犹豫。你’ll occasionally need to use volatile in C and C++, but in Java, you should not be using volatile unless you really know what you are doing. (Ironically the semantics of volatile in Java are more useful and are more clearly defined than they are in C and C++.)

In both languages, the volatile keyword is not really for your benefit; instead, it tells the compiler that it needs to expect that another source of control (another thread, the operating system, or the hardware itself) may be modifying data asynchronously, and therefore the compiler cannot make certain optimizations. The classic example in C is a blocking loop:

volatile bool ready_flag;

void wait_flag()
{
ready_flag = false;
while (!ready_flag)
pause_for_an_instant();
}

void signal_flag()
{
ready_flag = true;
}


Here, one thread can call wait_flag() 和 execute the while loop until a second thread calls signal_flag(). If the volatile qualifier is not there, then the compiler is free to optimize access to ready_flag 和 decide that since it has just set ready_flag = false; at the beginning of the loop, it can assume that ready_flag is always false 和 the while loop will never exit. Including volatile tells the compiler it must not make that assumption, and that each evaluation of the contents of the C variable ready_flag requires a memory read to its storage location. Similarly, any assignment to ready_flag actually requires a memory write. For example, suppose you have this in C:

volatile int answer_to_the_universe;

void something_or_other()
{
answer_to_the_universe = 54;
answer_to_the_universe = 42;
}


In this case, the compiler is required to write 54 to memory and then 42 to memory. If answer_to_the_universe weren’t declared as volatile, the compiler could look at this program and decide, “嘿，看，Moron程序员再次做蠢事。他’■写两次到相同的变量。第一次分配54次从未被使用过，所以我可以优化它。” But with volatile, it does exactly what you ask. This example isn’实际上是那么远的;一些嵌入式处理器需要某些背对背序列写入同一系统寄存器以解锁内存区域并允许它们更改。

In a system with an OS, pause_for_an_instant() should call an appropriate sleep() 或者 wait() function to release control to the OS. In low-level embedded systems without an OS, if signal_flag() is called in an interrupt service routine, and there’没有别的是主线代码，但等等，它’SOK使用旋转循环：

void wait_flag()
{
ready_flag = false;
while (!ready_flag)
;
}


Also, the volatile qualifier acts similarly to the const qualifier, in that it is a constraint. Variables marked volatile cannot be passed by reference to a function, unless that function’s argument is a volatile * 或者 volatile & (in C++). If we want to rewrite the wait_flag()signal_flag() functions so they don’T访问全局变量，我们’D必须使用此语法：

void wait_flag(volatile bool *pready_flag)
{
*pready_flag = false;
while (!*pready_flag)
pause_for_an_instant();
}

void signal_flag(volatile bool *pready_flag)
{
*pready_flag = true;
}


Unless we use the volatile qualifier in the function signature, we cannot pass in the address to a volatile variable to a function. If we remove the volatile, the compiler will report an error:

/* Wrong! This version of wait_flag()
* allows the compiler to optimize out
* the read access to pready_flag
* at the top of the while() loop
* incorrectly, and it will cause
* a compilation error if you call
* wait_flag() with a volatile pointer.
*/
void wait_flag_bad(bool *pready_flag)
{
*pready_flag = false;
while (!*pready_flag)
pause_for_an_instant();
}


Note that constvolatile 是 not exclusive: const just tells the compiler that you promise not to change the data, whereas volatile tells the compiler that something else might be changing the data. So functions that read peripheral registers by address, but do not write them, should look like this:

bool data_available (const volatile uint16_t *uart_status_ptr)
{
return (*uart_status_ptr & DATA_AVAILABLE_BIT) != 0;
}


The const keyword is optional; it helps the compiler check that you are faithful to your promise not to write to the pointer. The volatile keyword is required if you pass in the address to a volatile register.

### When else should you use volatile in C and C++?

The volatile keyword leads to these fringes… here’s的东西xc16用户’s Guide has to say about volatile:

Another use of the volatile keyword is to prevent variables being removed if they are not used in the C source. If a non-volatile variable is never used, or used in a 对程序没有影响的方式’s功能，然后可以在代码之前删除 由编译器生成。

function delay(uint16_t n)
{
uint16_t i;
for (i = 0; i < n; ++i)
;
}


The compiler is free to optimize out the loop because the variable i is never used. So one way that will fix this —至少对于XC16编译器;一世’不确定此技术是便携式的— is as follows:

function delay(uint16_t n)
{
volatile uint16_t i;
for (i = 0; i < n; ++i)
;
}


Another use of volatile in C is that it may help you prevent the compiler from reordering memory accesses among volatile variables. Maybe. From what I’读这是C标准HASN的那些东西之一’T清楚地记录了它’达到编译器的解释。

In Java, volatile has some 非常具体的语义: it essentially guarantees certain ordering constraints of memory reads and writes among threads. This is really hard to use correctly, and since there are plenty of built-in concurrency features, you should be using them rather than getting your fingers dirty with volatile.

（顺便说一下，其他计算机语言有一系列方法： Fortran. has a C-like volatile keyword; Python, Ruby, Go, and Rust do not, preferring to keep this behavior out of the core language and leave it up to standard libraries, which is probably the better choice.)

## 波动率304：尤达，内存模型和地下室的POSSE

I’除了指出一些关于进一步阅读的文章，不再对这个问题说更多有关这个问题。让’离开地下室的工作，唐’忘记了我们依靠它们很多。

## 包起来

• 计算机编程中的波动性是数据可以从程序之外的外部代理更改’S控制线程。该代理可以是另一个控制线程，或操作系统或计算硬件本身。 （特别是嵌入式微处理器中的控制寄存器。）
• The volatile qualifier in C has a couple of important aspects:
• 它用于表示受外部变化的程序变量。
• 它要求编译器在评估或分配程序变量时执行内存读取和写入。
• 它还可以防止编译器显然优化“useless”至少在某些编译器中计算。
• Functions accepting arguments that are pointers (or references in C++) that are not qualified with volatile can accept only non-volatile inputs. If the function signature has an argument qualified with volatile, it can accept pointers to either volatile 或者 non-volatile variables.
• The volatile keyword in Java is used to denote certain types of synchronization of memory accesses between threads. Don’你自己使用它;相反，使用更高级别的设施 java.lang.concurrent.
• 在C或Java程序中以特定顺序编写语句’t意味着编译器和处理器可以’只要它没有重新排序它们’t更改结果计算。了解内存访问重新排序中涉及的所有机制都很棘手，它’留给专家并更容易将其留给专家，而是使用以规定的方式设计和测试的库。

## 进一步阅读地下室

©2014年杰森M. Sachs，保留所有权利。