Intermediate
6.x
Kernel Threads, Part 3
← Previous Lecture | Next Lecture →
If you have ever written a kernel thread and wondered how to shut it down cleanly, this lecture is for you. Linux kernel thread cleanup is one of those topics that looks simple on the surface but hides a subtle trap: many beginner tutorials stop a kthread by sending it a signal such as SIGINT or SIGQUIT from user space. It works in a demo, but it is fragile and completely unsuitable for a real driver. In this lecture — part of our free Linux kernel development course — you will learn the correct, signal-free pattern that every production-quality kernel thread should use, why it matters, and how to implement it yourself on a modern 6.x kernel.
What You Will Learn
- Why relying on user-space signals to stop a kernel thread is a bad practice
- How
kthread_should_stop()gives you signal-free, race-free kernel thread cleanup - What actually happens internally when you call
kthread_stop() - How to interpret the integer return value of
kthread_stop() - A complete, original kernel thread example built and tested against kernel 6.x
- Common mistakes developers make when tearing down kernel threads
Prerequisites
This lecture assumes you are comfortable with the basics covered earlier in this free Linux device drivers course:
- Creating a kernel thread with
kthread_create()/kthread_run() - Building and loading a kernel module (
insmod,rmmod) - Basic process states (
TASK_RUNNING,TASK_INTERRUPTIBLE)
If any of these are unfamiliar, we recommend going through the earlier lectures in this free embedded systems course first.
Why Signals Are the Wrong Tool for Kernel Thread Termination
A common first attempt at stopping a kernel thread looks like this: allow a couple of signals inside the thread, put the thread to sleep, and have a human (or a script) send SIGINT or SIGQUIT from a shell to wake it up so it can exit. This “works” in a lab demo because you, the developer, remember to send the signal before unloading the module. But think about what happens the moment you forget.
If your module’s cleanup path calls kthread_stop() on a thread that is sleeping and has never been signaled, the rmmod command will appear to hang. It is not actually broken — kthread_stop() is patiently blocked inside wait_for_completion(), waiting for the thread to notice it should exit and return. Depending on the sleep state your thread is in, that wait can be indefinite. In a real product, an end user is never going to know they must send a specific signal before removing a driver. This makes signal-driven shutdown a non-starter outside of toy examples.
The Correct Pattern: kthread_should_stop() as the Loop Condition
The kernel provides a purpose-built mechanism for exactly this problem, and it requires no signals at all. The idiom is to structure your thread’s main loop around the kthread_should_stop() helper:
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ); /* sleep, but wake periodically to re-check */
}
set_current_state(TASK_RUNNING);
return 0;
kthread_should_stop() returns true exactly when someone has called kthread_stop() on this thread. Internally, kthread_stop() does three things:
- Sets an internal “should stop” flag on the thread’s
kthreadstructure - Wakes the thread up if it is currently sleeping
- Blocks the caller on a completion, waiting for the thread function to actually return
Because the wake-up is built into kthread_stop() itself, you do not need allow_signal(), you do not need a user to send anything from a shell, and there is no window where the thread can miss the request. This is what makes the pattern both simpler and safer than the signal-based approach.
Worked Example: A Signal-Free Kernel Thread on Kernel 6.x
Below is an original, minimal kernel module that demonstrates this pattern end to end. It spawns a thread that counts how many times it wakes up, sleeps using schedule_timeout() so it periodically checks for a stop request, and exits cleanly the moment kthread_stop() is called — no signal, no allow_signal(), no user interaction required.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala: clean kthread shutdown demo");
static struct task_struct *epk_thread;
static unsigned long epk_wake_count;
static int epk_thread_fn(void *data)
{
pr_info("epk_cleandemo: thread starting, pid=%d\n", current->pid);
while (!kthread_should_stop()) {
epk_wake_count++;
set_current_state(TASK_INTERRUPTIBLE);
/* Sleep for up to 2 seconds, but wake early if kthread_stop()
* calls wake_up_process() on us. */
schedule_timeout(msecs_to_jiffies(2000));
}
set_current_state(TASK_RUNNING);
pr_info("epk_cleandemo: stop requested, exiting after %lu wakeups\n",
epk_wake_count);
return 0; /* becomes the return value of kthread_stop() */
}
static int __init epk_cleandemo_init(void)
{
epk_thread = kthread_run(epk_thread_fn, NULL, "epk_cleandemo");
if (IS_ERR(epk_thread)) {
pr_err("epk_cleandemo: failed to create kernel thread\n");
return PTR_ERR(epk_thread);
}
pr_info("epk_cleandemo: module loaded\n");
return 0;
}
static void __exit epk_cleandemo_exit(void)
{
int ret = kthread_stop(epk_thread);
pr_info("epk_cleandemo: kthread_stop() returned %d\n", ret);
pr_info("epk_cleandemo: module unloaded, no signal was ever sent\n");
}
module_init(epk_cleandemo_init);
module_exit(epk_cleandemo_exit);
Try it yourself on a 6.x kernel:
$ make
$ sudo insmod epk_cleandemo.ko
$ dmesg | tail -3
$ sudo rmmod epk_cleandemo
$ dmesg | tail -3
You will notice that rmmod returns almost immediately — because schedule_timeout() wakes the thread quickly once kthread_stop() calls wake_up_process() internally, and the loop condition catches the stop request on the very next check.
Understanding the Return Value of kthread_stop()
kthread_stop() is not a void function — it returns an int, and that integer is exactly what your thread function returned. This lets your cleanup code learn whether the thread’s work actually completed successfully.
| Thread function returned | kthread_stop() returns | Meaning |
|---|---|---|
| 0 | 0 | Thread completed its work normally |
| A negative errno (e.g. -EIO) | Same errno | Thread’s work failed for a specific reason |
| Thread never got scheduled even once | -EINTR | Thread was stopped before it ever ran |
Logging this return value in your module’s exit path — as the example above does — is a cheap and effective way to catch bugs during development.
Common Mistakes When Terminating Kernel Threads
- Calling kthread_stop() before the thread has started running. This is safe — the kernel handles it — but if your thread function has a startup delay before it ever checks
kthread_should_stop(), be aware the call will simply wait longer. - Forgetting set_current_state(TASK_RUNNING) after waking up. Leaving the task state as
TASK_INTERRUPTIBLEwhen you break out of the loop can confuse the scheduler and any code inspecting the task’s state. - Using an unbounded schedule() instead of schedule_timeout(). If your thread never wakes up on its own, only an explicit wake-up will free it — make sure something (an event, a timer, or kthread_stop() itself) is guaranteed to wake it.
- Checking kthread_should_stop() only once at the top of a long-running function. If your thread does a lot of work per iteration, check the flag frequently so shutdown remains responsive.
- Mixing the signal-based approach with kthread_should_stop(). Pick one pattern. Combining
allow_signal()with kthread_should_stop() is valid for advanced cases (covered in an earlier lecture), but for straightforward worker threads the signal-free pattern shown here is simpler and less error-prone.
Best Practices for Kernel Thread Shutdown
- Default to
kthread_should_stop()as your loop condition unless you have a specific reason to handle signals. - Use
schedule_timeout()rather than an indefiniteschedule()so the thread periodically re-checks the stop flag even without an explicit wake-up source. - Always call
set_current_state(TASK_RUNNING)immediately after breaking out of the sleep loop. - Log the return value of
kthread_stop()during development to catch unexpected failures early. - Keep the per-iteration work short, or check
kthread_should_stop()inside long-running work so shutdown stays responsive.
Performance and Security Considerations
Performance: Checking kthread_should_stop() is a cheap, non-blocking read of an atomic-like flag, so calling it frequently inside a loop has negligible overhead. Prefer a short schedule_timeout() over busy-waiting, which would waste CPU cycles and hurt overall system throughput.
Security: Because this pattern removes the need to accept signals from user space entirely, it also removes a potential (if narrow) avenue for an unprivileged process to interfere with your thread’s lifecycle. A driver that only reacts to kthread_stop() called from its own module code has a smaller, better-defined control surface than one that reacts to arbitrary signals.
Real-World Use Case
This exact pattern is the backbone of countless in-kernel worker threads — from background flush threads in filesystems to polling threads in device drivers that periodically check hardware status registers. Any time a driver needs a long-lived background task that must shut down cleanly and deterministically when the module is removed, this is the idiom to reach for.
Summary: Key Takeaways
- Never rely on user-space signals to stop a kernel thread in production code.
- Structure the thread’s main loop around
while (!kthread_should_stop()). kthread_stop()sets a flag, wakes the thread, and waits for it to return.- The integer returned by
kthread_stop()is the thread function’s own return value, or-EINTRif the thread never ran. - Use
schedule_timeout()instead of an unboundedschedule()so shutdown stays responsive.
Conclusion
Getting kernel thread cleanup right is a small detail with outsized consequences — get it wrong, and your driver can hang the system on every module removal. The good news is that the correct approach is also the simpler one: build your thread around kthread_should_stop(), let kthread_stop() do the waking and waiting for you, and skip signals altogether. In the next lecture in this free Linux kernel development course, we will build on this foundation by combining a kernel thread with a kernel timer to detect whether a piece of work completed within a deadline — a pattern used heavily in real device drivers that must bound how long an operation is allowed to take.
Frequently Asked Questions
Q1. Do I ever need to send a signal to stop a kernel thread?
No. For most worker threads, kthread_should_stop() combined with kthread_stop() is all you need. Signal handling inside a kthread is reserved for more specialized cases.
Q2. What happens if I call kthread_stop() twice on the same thread?
This is not safe. Once a thread has exited and been reaped, calling kthread_stop() again on the same (now-invalid) task pointer is undefined behavior. Always ensure it is called exactly once per thread lifetime.
Q3. Can kthread_stop() be called from an interrupt context?
No. It can block (it waits on a completion), so it must be called from process context, typically your module’s exit function.
Q4. Why does my rmmod hang when I use this pattern?
Usually because the thread is sleeping in a state that is never woken up independently of kthread_stop(), and your loop does not re-check kthread_should_stop() after being woken. Double-check that the wake path leads back to the top of the while loop.
Q5. Is kthread_should_stop() safe to call from multiple places in my thread function?
Yes, it is just a flag check and can be called as often as needed.
Q6. Does this pattern work the same way on older kernels?
The core kthread_should_stop() / kthread_stop() API has been stable for a very long time and behaves the same way on kernel 6.x as described here.
Q7. What is the difference between kthread_should_stop() and kthread_should_park()?
kthread_should_stop() signals permanent termination. kthread_should_park() is a separate, temporary “pause” mechanism used mainly by CPU hotplug code and is outside the scope of this lecture.
Q8. Do I still need this if I use a workqueue instead of a raw kernel thread?
Workqueues manage their own worker thread lifecycle for you, so you generally will not call kthread_should_stop() directly. We cover workqueues as a separate, simpler alternative later in this course.
Continue the Free Linux Kernel Development Course
More free lectures on kernel threads, timers, and device drivers are on the way.
Browse the Full Course Next Lecture →
2 Comments