This article is a continuation of the Series on Linux Device Driver and carries the discussion on Linux device drivers and their implementation. The aim of this series is to provide easy and practical examples that anyone can understand.
In this article, we are going to focus on Kernel Thread. It covers the concepts of processes and threads in the Linux operating system, emphasizing their differences and advantages. The article delves into kernel thread programming, explaining functions and techniques for managing kernel threads.
It also includes source code examples for creating and controlling kernel threads within a device driver. The provided code demonstrates how to initiate and stop kernel threads and how they can be used in Linux device driver development.
You can also read mutex, read-write spinlock, Procfs, Workqueue, Completion, Softirq, and threaded IRQ in the Linux device driver.
Process
An executing instance of a program is called a process. Some operating systems use the term ‘task‘ to refer to a program that is being executed. The process is a heavyweight process. The context switch between the process is time-consuming.
Threads
A thread is an independent flow of control that operates within the same address space as other independent flows of control within a process.
One process can have multiple threads, with each thread executing different code concurrently while sharing data and synchronizing much more easily than cooperating processes.
Threads require fewer system resources than processes and can start more quickly. Threads, also known as lightweight processes.
One of the advantages of the thread is that since all the threads within the processes share the same address space, the communication between the threads is far easier and less time-consuming as compared to processes.
This approach has one disadvantage also. It leads to several concurrency issues and requires synchronization mechanisms to handle the same.
Thread Management
Whenever we are creating a thread, it has to be managed by someone. So management follows like below.
- A thread is a sequence of instructions.
- CPU can handle one instruction at a time.
- To switch between instructions on parallel threads, the execution state needs to be saved.
- Execution state in its simplest form is a program counter and CPU registers.
- The program counter tells us what instruction to execute next.
- CPU registers hold execution arguments, for example, addition operands.
- This alternation between threads requires management.
- Management includes saving state, restoring state, and deciding what thread to pick next.
Types of Thread
There are two types of threads.
- User Level Thread
- Kernel Level Thread
1. User Level Thread
In this type, the kernel is not aware of these threads. Everything is maintained by the user thread library. That thread library contains code for creating and destroying threads, for passing messages and data between threads, for scheduling thread execution, and for saving and restoring thread contexts. So all will be in User Space.
2. Kernel Level Thread
Kernel-level threads are managed by the OS, therefore, thread operations are implemented in the kernel code. There is no thread management code in the application area.
Anyhow each type of thread has advantages and disadvantages too.
Now we will move into Kernel Thread Programming. First, we will see the functions used in a kernel thread.
Kernel Thread Management Functions
There are many functions used in Kernel Thread. We will see each one by one. We can classify those functions based on functionalities.
- Create Kernel Thread
- Start Kernel Thread
- Stop Kernel Thread
- Other functions in Kernel Thread
To use the below functions, you should include linux/kthread.h
header file.
Create Kernel Thread
kthread_create
This API creates a kthread.
Where,
This helper function creates and names a kernel thread. But we need to wake up that thread manually. When woken, the thread will run
It Returns |
Start Kernel Thread
wake_up_process
This is used to Wake up a specific process.int wake_up_process (struct task_struct * p);
Where,
Attempt to wake up the nominated process and move it to the set of runnable processes. It returns 1 if the process was woken up, 0 if it was already running. It may be assumed that this function implies a write memory barrier before changing the task state if and only if any tasks are woken up. |
Stop Kernel Thread
kthread_stop
It stops a thread created by kthread_create .
Where,
Sets It Returns the result of |
Other Functions in Kernel Thread
kthread_should_stop
should this kthread return now?
When someone calls |
kthread_bind
This is used to bind a just-created kthread to a CPU.
Where,
|
Implementation
Thread Function
First, we have to create our thread that has the argument of void *
and should return int
value. We should follow some conditions in our thread function. It is advisable.
- If that thread is a long-run thread, we need to check
kthread_should_stop()
every time, because any function may callkthread_stop
. If any function calledkthread_stop
, that timekthread_should_stop
will returntrue
. We have to exit our thread function iftrue
value has been returned bykthread_should_stop
. - But if your thread function is not running long, then let that thread finish its task and kill itself using
do_exit
.
In my thread function, let’s print something every minute and it is a continuous process. So let’s check the kthread_should_stop
every time. See the below snippet to understand.
int thread_function(void *pv) { int i=0; while(!kthread_should_stop()) { printk(KERN_INFO "In EmbeTronicX Thread Function %d\n", i++); msleep(1000); } return 0; }
Creating and Starting Kernel Thread
So as of now, we have our thread function to run. Now, we will create a kernel thread using kthread_create
and start the kernel thread using wake_up_process
.
static struct task_struct *etx_thread; etx_thread = kthread_create(thread_function,NULL,"eTx Thread"); if(etx_thread) { wake_up_process(etx_thread); } else { printk(KERN_ERR "Cannot create kthread\n"); }
There is another function that does both processes (create and start). That is kthread_run()
. You can replace both kthread_create
and wake_up_process
using this function.
kthread_run
This is used to create and wake a thread.
Where,
Convenient wrapper for It returns the |
You can see the below snippet which using kthread_run
.
static struct task_struct *etx_thread; etx_thread = kthread_run(thread_function,NULL,"eTx Thread"); if(etx_thread) { printk(KERN_ERR "Kthread Created Successfully...\n"); } else { printk(KERN_ERR "Cannot create kthread\n"); }
Stop Kernel Thread
You can stop the kernel thread using kthread_stop
. Use the below snippet to stop.
kthread_stop(etx_thread);
Kernel Thread in Linux Driver – Source Code
The kernel thread will start when we insert the kernel module. It will print something every second. When we remove the module that time it stops the kernel thread. Let’s see the source code.
[Get the source code from the GitHub]
/***************************************************************************//** * \file driver.c * * \details Simple Linux device driver (Kernel Thread) * * \author EmbeTronicX * * \Tested with Linux raspberrypi 5.10.27-v7l-embetronicx-custom+ * *******************************************************************************/ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/kdev_t.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include<linux/slab.h> //kmalloc() #include<linux/uaccess.h> //copy_to/from_user() #include <linux/kthread.h> //kernel threads #include <linux/sched.h> //task_struct #include <linux/delay.h> #include <linux/err.h> dev_t dev = 0; static struct class *dev_class; static struct cdev etx_cdev; static int __init etx_driver_init(void); static void __exit etx_driver_exit(void); static struct task_struct *etx_thread; /* ** Function Prototypes */ /*************** Driver functions **********************/ static int etx_open(struct inode *inode, struct file *file); static int etx_release(struct inode *inode, struct file *file); static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off); static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off); /******************************************************/ int thread_function(void *pv); /* ** Thread */ int thread_function(void *pv) { int i=0; while(!kthread_should_stop()) { pr_info("In EmbeTronicX Thread Function %d\n", i++); msleep(1000); } return 0; } /* ** File operation sturcture */ static struct file_operations fops = { .owner = THIS_MODULE, .read = etx_read, .write = etx_write, .open = etx_open, .release = etx_release, }; /* ** This function will be called when we open the Device file */ static int etx_open(struct inode *inode, struct file *file) { pr_info("Device File Opened...!!!\n"); return 0; } /* ** This function will be called when we close the Device file */ static int etx_release(struct inode *inode, struct file *file) { pr_info("Device File Closed...!!!\n"); return 0; } /* ** This function will be called when we read the Device file */ static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off) { pr_info("Read function\n"); return 0; } /* ** This function will be called when we write the Device file */ static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) { pr_info("Write Function\n"); return len; } /* ** Module Init function */ static int __init etx_driver_init(void) { /*Allocating Major number*/ if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){ pr_err("Cannot allocate major number\n"); return -1; } pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev)); /*Creating cdev structure*/ cdev_init(&etx_cdev,&fops); /*Adding character device to the system*/ if((cdev_add(&etx_cdev,dev,1)) < 0){ pr_err("Cannot add the device to the system\n"); goto r_class; } /*Creating struct class*/ if(IS_ERR(dev_class = class_create(THIS_MODULE,"etx_class"))){ pr_err("Cannot create the struct class\n"); goto r_class; } /*Creating device*/ if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){ pr_err("Cannot create the Device \n"); goto r_device; } etx_thread = kthread_create(thread_function,NULL,"eTx Thread"); if(etx_thread) { wake_up_process(etx_thread); } else { pr_err("Cannot create kthread\n"); goto r_device; } #if 0 /* You can use this method also to create and run the thread */ etx_thread = kthread_run(thread_function,NULL,"eTx Thread"); if(etx_thread) { pr_info("Kthread Created Successfully...\n"); } else { pr_err("Cannot create kthread\n"); goto r_device; } #endif pr_info("Device Driver Insert...Done!!!\n"); return 0; r_device: class_destroy(dev_class); r_class: unregister_chrdev_region(dev,1); cdev_del(&etx_cdev); return -1; } /* ** Module exit function */ static void __exit etx_driver_exit(void) { kthread_stop(etx_thread); device_destroy(dev_class,dev); class_destroy(dev_class); cdev_del(&etx_cdev); unregister_chrdev_region(dev, 1); pr_info("Device Driver Remove...Done!!\n"); } module_init(etx_driver_init); module_exit(etx_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("EmbeTronicX <[email protected]>"); MODULE_DESCRIPTION("A simple device driver - Kernel Thread"); MODULE_VERSION("1.14");
MakeFile
obj-m += driver.o KDIR = /lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(shell pwd) modules clean: make -C $(KDIR) M=$(shell pwd) clean
Building and Testing Driver
- Build the driver by using Makefile (
sudo make
) - Load the driver using
sudo insmod driver.ko
- Then Check the
dmesg
Major = 246 Minor = 0 Device Driver Insert...Done!!! In EmbeTronicX Thread Function 0 In EmbeTronicX Thread Function 1 In EmbeTronicX Thread Function 2 In EmbeTronicX Thread Function 3
- So our thread is running now.
- Remove the driver using
sudo rmmod driver
to stop the thread.
In our next tutorial, we will discuss the tasklet in the Linux device driver.
Please find the other Linux device driver tutorials here.
You can also read the below tutorials.

Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!