Skip to main content

Volatile Keyword in C


Few days back I came across this question, searched web to find complete answer explaining every details. However, the answers were scattered at many places So I thought of accumulating the answers to have complete picture of this question. So lets dive in!

Volatile in C actually came into existence for the purpose of not caching the values of variable automatically. It will tell the machine not to cache the value of this variable. So it will take the value of the given volatile variable from the main memory every time it encounters it. This mechanism is used because at any time the value can be modified by the OS or any interrupt. So using volatile will help us accessing the value afresh every time.

Syntax:
To declare a variable volatile, include keyword volatile before or after the data type:

// Declaration of variable
int volatile foo;
volatile int foo;

Use:
A variable should be declared volatile whenever its value can change unexpectedly. In practice, there are three cases:

  • Memory mapped peripheral register
  • Global variable modified by ISR
  • Global variable in multi-threaded application
Peripheral Registers:
Embedded system contains real hardware, usually with sophisticated peripherals. These peripherals contain registers, whose value may change asynchronously with program flow. As a very simple example consider an 8-bit register at address 0x1234. It is required that you poll that register until its status becomes non-zero.

The naive and incorrect implementation is as:
UINT1 *ptr = (UINT1 *)0x1234;
// Wait for register to become non-zero.
while(*ptr == 0);
// Do something

This statement will almost certainly fail, if  you turn on the optimizer, Since the compiler will generate assembly that will look something like this:

mov ptr, 0x1234
mov a, @ptr
loop bz loop

The rationale of optimizer is quite simple: having already read the value into the accumulator(second line), there is no need to re-read it. Thus, in third line we end up in infinite loop.

To force the compiler what we want, we modify the declaration to:
UINT1 volatile *ptr = (UINT1 volatile *)0x1234;
// Wait for register to become non-zero.
while(*ptr == 0);
// Do something

The corresponding assembly language now looks like:

mov ptr, 0x1234
loop mov a, @ptr
bz loop

Thus desired behavior is achieved.

Comments

Popular posts from this blog

Spinlock implementation in ARM architecture

SEV and WFE are the main instructions used for implementing spinlock in case of ARM architecture . Let's look briefly at those two instructions before looking into actual spinlock implementation. SEV SEV causes an event to be signaled to all cores within a multiprocessor system. If SEV is implemented, WFE must also be implemented. Let's look briefly at those two instructions before looking into actual spinlock implementation. WFE If the Event Register is not set, WFE suspends execution until one of the following events occurs: an IRQ interrupt, unless masked by the CPSR I-bit an FIQ interrupt, unless masked by the CPSR F-bit an Imprecise Data abort, unless masked by the CPSR A-bit a Debug Entry request, if Debug is enabled an Event signaled by another processor using the SEV instruction. In case of  spin_lock_irq( )/spin_lock_irqsave( ) , as IRQs are disabled, the only way to to resume after WFE intruction has executed is to execute SEV ins

Explanation of "struct task_struct"

This document tries to explain clearly what fields in the structure task_struct do. It's not complete and everyone is welcome to add information. Let's start by saying that each process under Linux is defined by a structure task_struct. The following information are available (on kernel 2.6.7): - volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */ - struct thread_info *thread_info; a pointer to a thread_info... - atomic_t usage; used by get_task_struct(). It's also set in kernel/fork.c. This value acts like a reference count on the task structure of a process. It can be used if we don't want to hold the tasklist_lock. - unsigned long flags;    /* per process flags, defined below */ process flag can be, for example, PF_DEAD when exit_notify() is called. List is of possible values is in include/linux/sched.h - unsigned long ptrace; used by ptrace a system call that provides the ability to a parent process to observe and con

Macro "container_of"

Understanding container_of macro in the Linux kernel When you begin with the kernel, and you start to look around and read the code, you will eventually come across this magical preprocessor construct. What does it do? Well, precisely what its name indicates. It takes three arguments – a  pointer ,  type  of the container, and the  name  of the member the pointer refers to. The macro will then expand to a new address pointing to the container which accommodates the respective member. It is indeed a particularly clever macro, but how the hell can this possibly work? Let me illustrate… The first diagram illustrates the principle of the  container_of(ptr, type, member)  macro for who might find the above description too clumsy. I llustration of how the containter_of macro works. Bellow is the actual implementation of the macro from Linux Kernel: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (