Linux Kernel – atomic operations

Non-atomic operations on a memory usually perform a read from memory and later modify/write to the memory to complete a memory update. This can lead to race conditions to occur for a shared resource

Atomic operations on the other hand provide instructions which complete in one instruction cycle. Since atomic instructions complete in one single instruction cycle, they are not interrupted by other CPUs trying to access the same memory location. This prevents race conditions.

Linux provides two types of atomic operations –

  • atomic operations on integer variables and
  • atomic operations that operate on individual bits

Atomic operation on integer variables

When a ‘C’ operation like below:

        “a= a+1” is performed, the linux kernel cannot guarantee that the operation will complete atomically. To distinguish between the need for atomic operations and non-atomic operations, the Linux kernel provides a specific atomic data type – “atomic_t“. The way to declare an atomic_t variable is shown below

atomic_t variable; /* variable is used for atomic operations only*/

The Linux kernel provides different atomic functions that perform operations on atomic variables. A few atomic operation functions provided in the Linux kernel are listed below:

  • atomic_set(atomic_t *a, int value); /* set the value at memory location a */ 
  • atomic_add(int val, atomic_t *a); /*add val to value at memory location a */
  • atomic_read(const atomic_t *a); /* read the atomic_t value at a*/
  • atomic_inc(atomic_t *a) /* increment the value at a atomically */
  • atomic_dec(atomic_t *a) /* decrement the value at a atomically */
  • atomic_sub(int val, atomic_t *a) /* subtract the value at a by amount val */ 

Atomic operation on individual bits

A number of Linux kernel APIs perform operation on individual bits of a variable. The bit operations do not need “atomic_t” data type. A few examples are provided below

  •  set_bit(long nr, volatile unsigned long * addr) /* atomically set the bit in memory */
  • clear_bit(long nr, volatile unsigned long * addr) /* atomically clear the bit in memory */
  • change_bit(long nr, volatile unsigned long * addr) /*  toggle a bit in memory */
  • test_and_set_bit(long nr, volatile unsigned long * addr) /* set the bit and return its old value */
  • test_and_clear_bit(long nr, volatile unsigned long * addr) /* clear the bit and return its old value */

For a more comprehensive list of APIs and to gain more information on Linux atomic operations, do refer the below links

Linux – atomic operations – Sample code

 

Comments

  1. Pingback: Linux-Locking Mechanisms – Concurrency and race conditions – part 2 | Hitch Hiker's Guide to Learning

Leave a Reply

Your email address will not be published. Required fields are marked *