Linux Kernel Synchronization: ISA, Critical Sections, Spinlocks and Atomic Operations

Search for a command to run...

No comments yet. Be the first to comment.
Modern software systems often separate control logic from high-performance execution logic. This design is common in networking, distributed systems, operating systems, storage engines, and embedded s

Introduction Engineering software often combines multiple programming languages to leverage their individual strengths. A common approach is to implement computational algorithms in native C or C++ wh

Deadlocks are one of the most common synchronization problems encountered in operating systems and concurrent programming. Although the concept is frequently introduced in textbooks, observing it insi

Memory and resource allocation are fundamental operations inside the Linux kernel. Whether assigning device IDs, managing CPU masks, allocating interrupt vectors, or tracking hardware resources, the k

The Linux scheduler is one of the most important components of the operating system. Every running program, background service, and kernel thread eventually interacts with the scheduler. In this artic

Writing Linux kernel code is very different from writing user-space applications. The kernel runs with direct access to hardware, supports multiple CPUs, handles interrupts, and manages shared resources that may be accessed concurrently.
Because of this, synchronization becomes a fundamental requirement. Without proper synchronization, race conditions, data corruption, and system instability can occur.
In this article, I explore several important Linux kernel concepts through a simple kernel module:
ISA (Instruction Set Architecture) detection
Critical sections
Exclusive execution
Interrupt-safe locking
Atomic counters
Global shared variables
Spinlocks
| Concept | Mechanism |
|---|---|
| ISA Detection | CONFIG_X86 / CONFIG_ARM64 |
| Critical Section | Protected code block |
| Exclusive Execution | Spinlock |
| Interrupt Safety | spin_lock_irqsave() |
| Atomic Operations | atomic_t |
| Shared Resource | Global Counter |
| Logging | printk() |
| Module Lifecycle | module_init() / module_exit() |
An Instruction Set Architecture (ISA) defines the machine instructions that a processor understands.
Common examples include:
x86_64
ARM64
RISC-V
MIPS
The Linux kernel is compiled for a specific architecture. Sometimes kernel code must behave differently depending on the target processor.
A simple ISA detection example:
#if defined(CONFIG_X86)
printk(KERN_INFO "ISA = x86\n");
#elif defined(CONFIG_ARM64)
printk(KERN_INFO "ISA = ARM64\n");
#endif
This allows architecture-specific behavior inside kernel modules and drivers.
A critical section is a region of code that accesses shared data.
Example:
global_counter++;
Although this looks like a single operation, it actually involves multiple CPU instructions:
Read memory
Increment value
Write result back
If two CPUs execute this sequence simultaneously, updates can be lost.
This problem is known as a race condition.
Critical sections must therefore be protected to ensure correctness.
Exclusive execution means only one execution context may enter a critical section at a time.
In Linux kernel development, this is commonly achieved using synchronization primitives such as:
Spinlocks
Mutexes
Semaphores
RW Locks
In this example, a spinlock is used.
spin_lock_irqsave(&counter_lock, flags);
/* critical section */
spin_unlock_irqrestore(&counter_lock, flags);
This guarantees exclusive access to shared resources.
The module contains a shared global variable:
static int global_counter;
Because global variables can be accessed by multiple CPUs or kernel threads, they must be protected when modified.
Example:
global_counter++;
Without synchronization:
Lost updates
Race conditions
Data corruption
With synchronization:
Predictable behavior
Correct results
Safe concurrent access
Global variables are often the first place where synchronization bugs appear.
A spinlock is a lightweight synchronization mechanism used inside the kernel.
Initialization:
spin_lock_init(&counter_lock);
Lock acquisition:
spin_lock_irqsave(&counter_lock, flags);
Lock release:
spin_unlock_irqrestore(&counter_lock, flags);
Unlike mutexes, a spinlock does not put the thread to sleep. Instead, the CPU continuously checks until the lock becomes available.
Spinlocks are therefore suitable for:
Short critical sections
Interrupt contexts
Performance-sensitive kernel code
They are not suitable for long operations.
Kernel code may be interrupted at almost any time.
Sources of interrupts include:
Keyboard
Mouse
Network devices
Storage devices
System timers
If both an interrupt handler and normal kernel code access the same data, race conditions may occur.
To prevent this, the module uses:
spin_lock_irqsave(&counter_lock, flags);
This operation:
Saves interrupt state
Disables local interrupts
Acquires the lock
After the critical section:
spin_unlock_irqrestore(&counter_lock, flags);
This restores the original interrupt state.
The result is safe access even when interrupts are involved.
Linux provides atomic data types for operations that must occur as indivisible actions.
Example:
static atomic_t atomic_counter =
ATOMIC_INIT(0);
Increment:
atomic_inc(&atomic_counter);
Read:
atomic_read(&atomic_counter);
Atomic operations eliminate race conditions for simple counter updates.
Benefits include:
No lost updates
No partial modifications
CPU-level atomic guarantees
They are commonly used for:
Reference counters
Statistics
Resource tracking
State flags
| Feature | Global Counter | Atomic Counter |
|---|---|---|
| Shared Resource | Yes | Yes |
| Requires Protection | Yes | Often No |
| Race Condition Risk | High | Low |
| Lock Required | Usually | Usually Not |
| Performance | Lower | Higher |
Atomic counters are ideal for simple increment/decrement operations.
More complex operations may still require locks.
The kernel equivalent of printf() is printk().
Example:
printk(KERN_INFO
"critical section entered\n");
Messages can be viewed using:
dmesg
This is one of the most important debugging techniques in kernel development.
Every Linux kernel module typically contains two entry points.
Initialization:
module_init(demo_init);
Cleanup:
module_exit(demo_exit);
Initialization runs when:
sudo insmod big_kernel_demo.ko
Cleanup runs when:
sudo rmmod big_kernel_demo
This lifecycle allows modules to allocate and release resources safely.
This small kernel module demonstrates several core synchronization concepts used throughout Linux kernel development.
I learned about:
ISA detection
Critical sections
Exclusive execution
Global shared variables
Spinlocks
Interrupt-safe locking
Atomic counters
Kernel logging
Module lifecycle management
These concepts form the foundation of:
Device driver development
Embedded Linux
Operating systems
Multicore programming
Linux security engineering
Low-level cybersecurity research
Mastering these fundamentals makes it easier to understand advanced topics such as semaphores, mutexes, RCU, memory barriers, lock-free programming, and kernel concurrency design.
GitHub Repository: