# Understanding Linux Kernel Mutex Synchronization with Kernel Threads

# Introduction

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
    

* * *

# The Problem: Shared Data

Consider a global variable shared by multiple kernel threads:

```c
static int shared_resource = 0;
```

Both threads increment this variable repeatedly.

```c
shared_resource++;
```

At first glance this looks harmless, but the operation is actually composed of multiple CPU instructions:

1.  Read value
    
2.  Increment value
    
3.  Write value back
    

If two threads perform these steps simultaneously, updates can be lost.

* * *

# Understanding Race Conditions

Without synchronization:

```text
Thread-1 reads 5
Thread-2 reads 5

Thread-1 writes 6
Thread-2 writes 6
```

Expected result:

```text
5 → 6 → 7
```

Actual result:

```text
5 → 6
```

One increment disappears.

This is known as a **race condition**.

* * *

# Enter the Mutex

Linux provides mutexes to guarantee mutual exclusion.

The module defines a mutex as:

```c
static DEFINE_MUTEX(my_shared_mutex);
```

A mutex ensures that only one thread can enter a critical section at a time.

* * *

# Critical Section Protection

The shared counter update is wrapped by the mutex:

```c
if (mutex_lock_interruptible(&my_shared_mutex))
    return -EINTR;

shared_resource++;

mutex_unlock(&my_shared_mutex);
```

This guarantees that concurrent updates occur safely.

* * *

# Why Use mutex\_lock\_interruptible()

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:

```c
mutex_lock_interruptible(&my_shared_mutex);
```

Unlike `mutex_lock()`, the interruptible version can abort waiting and return an error if a signal arrives.

* * *

# Return Values

A successful lock acquisition returns:

```c
0
```

If interrupted before obtaining the lock:

```c
-EINTR
```

This allows the thread to exit gracefully rather than waiting forever.

* * *

# Creating Kernel Threads

The module launches two kernel threads.

```c
thread1 = kthread_run(
    my_kthread_func,
    "Thread-1",
    "kthread_one");
```

```c
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
    

* * *

# Why task\_struct Matters

Thread handles are stored as:

```c
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
    

* * *

# Graceful Thread Shutdown

The worker loop uses:

```c
while (!kthread_should_stop())
```

When the module unloads:

```c
kthread_stop(thread1);
kthread_stop(thread2);
```

The threads detect the stop request and exit cleanly.

This is the recommended Linux kernel thread shutdown pattern.

* * *

# Sleeping Locks

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.

* * *

# Mutex Ownership Rules

A mutex has strict ownership semantics.

Correct:

```c
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.

* * *

# Expected Kernel Output

After loading the module:

```text
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.

# Key Takeaways

*   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**](https://github.com/aj333git/linux_kernel_mutex2) Explore the complete source code, build files, and module implementation on GitHub.
