Understanding Linux Kernel Mutex Synchronization with Kernel Threads

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

Concurrency is everywhere inside the Linux kernel. Multiple execution contexts may attempt to access the same data simultaneously, leading to race conditions and corrupted state.
In this article, I build a simple kernel module that launches two kernel threads and protects a shared global counter using a mutex. Along the way, I explore:
Kernel threads
Shared resources
Critical sections
Race conditions
Mutex synchronization
mutex_lock_interruptible()
Thread lifecycle management
Consider a global variable shared by multiple kernel threads:
static int shared_resource = 0;
Both threads increment this variable repeatedly.
shared_resource++;
At first glance this looks harmless, but the operation is actually composed of multiple CPU instructions:
Read value
Increment value
Write value back
If two threads perform these steps simultaneously, updates can be lost.
Without synchronization:
Thread-1 reads 5
Thread-2 reads 5
Thread-1 writes 6
Thread-2 writes 6
Expected result:
5 → 6 → 7
Actual result:
5 → 6
One increment disappears.
This is known as a race condition.
Linux provides mutexes to guarantee mutual exclusion.
The module defines a mutex as:
static DEFINE_MUTEX(my_shared_mutex);
A mutex ensures that only one thread can enter a critical section at a time.
The shared counter update is wrapped by the mutex:
if (mutex_lock_interruptible(&my_shared_mutex))
return -EINTR;
shared_resource++;
mutex_unlock(&my_shared_mutex);
This guarantees that concurrent updates occur safely.
Linux provides two common mutex acquisition APIs:
| API | Interruptible | Behavior |
|---|---|---|
mutex_lock() |
No | Waits indefinitely |
mutex_lock_interruptible() |
Yes | Can be interrupted by signals |
Example:
mutex_lock_interruptible(&my_shared_mutex);
Unlike mutex_lock(), the interruptible version can abort waiting and return an error if a signal arrives.
A successful lock acquisition returns:
0
If interrupted before obtaining the lock:
-EINTR
This allows the thread to exit gracefully rather than waiting forever.
The module launches two kernel threads.
thread1 = kthread_run(
my_kthread_func,
"Thread-1",
"kthread_one");
thread2 = kthread_run(
my_kthread_func,
"Thread-2",
"kthread_two");
Each thread repeatedly:
Acquires the mutex
Updates the counter
Releases the mutex
Sleeps briefly
Repeats
Thread handles are stored as:
static struct task_struct *thread1;
static struct task_struct *thread2;
Every Linux task is represented internally by a task_struct.
These handles allow the module to:
Stop threads
Manage thread lifecycle
Track execution state
The worker loop uses:
while (!kthread_should_stop())
When the module unloads:
kthread_stop(thread1);
kthread_stop(thread2);
The threads detect the stop request and exit cleanly.
This is the recommended Linux kernel thread shutdown pattern.
Mutexes are sleeping locks.
This means they may block and schedule another task while waiting.
Because of this, mutexes are appropriate in:
Kernel threads
Process context
System call paths
They are not suitable for:
Interrupt handlers
SoftIRQs
Atomic contexts
Sleeping is forbidden in those environments.
A mutex has strict ownership semantics.
Correct:
mutex_lock(&lock);
/* critical section */
mutex_unlock(&lock);
The same thread that acquires the lock must release it.
Violating this rule can trigger kernel warnings and undefined behavior.
After loading the module:
Initializing Mutex Kthread Module
Thread-1: Secured lock. Shared Resource value = 1
Thread-2: Secured lock. Shared Resource value = 2
Thread-1: Secured lock. Shared Resource value = 3
The counter increases sequentially because only one thread owns the mutex at any given moment.
Shared kernel data requires synchronization.
Race conditions occur when multiple threads modify data concurrently.
Mutexes provide mutual exclusion.
mutex_lock_interruptible() allows signal-aware waiting.
Mutexes are sleeping locks and should not be used in interrupt context.
Kernel threads are managed using task_struct.
kthread_stop() enables clean thread termination.
Understanding mutexes is one of the foundational steps toward mastering Linux kernel synchronization and building reliable concurrent kernel code.
GitHub Repository: 👉 linux_kernel_mutex Explore the complete source code, build files, and module implementation on GitHub.