In the previous article, we learned to write a simple kernel module. This was needed to kick start our journey into the Kernel Internals, as all of the code we are going to discuss, has to be pushed into the kernel as module. In this article, we will discuss about the Kernel Threads. So, lets begin…
Understanding Threads
Threads, also known as light weight processes are the basic unit of CPU initialization. So, why do we call them as light weight processes? One of the reason is that the context switch between the threads takes much lesser time as compared to processes, which results from the fact that all the threads within the process share the same address space, so you don’t need to switch the address space. In the user space, threads are created using the POSIX APIs and are known as pthreads. Some 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. And usually is done through the global variables. This approach has one disadvantage though. It leads to several concurrency issues and require the synchronization mechanisms to handle the same.
Having said that, why do we require threads? Need for the multiple threads arises, when we need to achieve the parallelism within the process. To give you the simple example, while working on the word processor, if we enable the spell and grammar check, as we key in the words, you will see red/green lines appear, if we type something syntactically/grammatically incorrect. This can most probably be implemented as threads.
Kernel Threads
Now, what are kernel threads? They are same as user space threads in many aspects, but one of the biggest difference is that they exist in the kernel space and execute in a privileged mode and have full access to the kernel data structures. These are basically used to implement background tasks inside the kernel. The task can be handling of asynchronous events or waiting for an event to occur. Device drivers utilize the services of kernel threads to handle such tasks. For example, the ksoftirqd/0 thread is used to implement the Soft IRQs in kernel. The khubd kernel thread monitors the usb hubs and helps in configuring usb devices during hot-plugging.
APIs for creating the Kernel thread
Below is the API for creating the thread:
#include <kthread.h>
kthread_create(int (*function)(void *data), void *data, const char name[], ...)
Parameters:
function – The function that the thread has to execute
data – The ‘data’ to be passed to the function
name – The name by which the process will be recognized in the kernel
Returns: Pointer to a structure of type task_struct
Below is an example code which creates a kernel thread:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
static struct task_struct *thread_st;
// Function executed by kernel thread
static int thread_fn(void *unused)
{
while (1)
{
printk(KERN_INFO "Thread Running\n");
ssleep(5);
}
printk(KERN_INFO "Thread Stopping\n");
do_exit(0);
return 0;
}
// Module Initialization
static int __init init_thread(void)
{
printk(KERN_INFO "Creating Thread\n");
//Create the kernel thread with name 'mythread'
thread_st = kthread_create(thread_fn, NULL, "mythread");
if (thread_st)
printk("Thread Created successfully\n");
else
printk(KERN_INFO "Thread creation failed\n");
return 0;
}
// Module Exit
static void __exit cleanup_thread(void)
{
printk("Cleaning Up\n");
}
In the above code, thread is created in the init_thread(). Created thread executes the function thread_fn(). Compile the above code and insert the module with insmod. Below is the output, you get:
Thread Created successfully
That’s all we get as an output. Now, you might be wondering why is thread_fn() not executing? The reason for this is, when we create the thread with kthread_create(), it creates the thread in sleep state and thus nothing is executed. So, how do we wake up the thread. We have a API wake_up_process() for this. Below is modified code which uses this API.
// Module Initialization static struct task_struct *thread_st; { printk(KERN_INFO "Creating Thread\n"); //Create the kernel thread with name 'mythread' thread_st = kthread_create(thread_fn, NULL, "mythread"); if (thread_st) { printk("Thread Created successfully\n"); wake_up_process(thread_st); } else printk(KERN_INFO "Thread creation failed\n"); return 0; }
As you might notice, wake_up_process() takes pointer to task_struct as an argument, which in turn is returned from kthread_create(). Below is the output:
Thread Created successfully Thread Running Thread Running ...
As seen, running a thread is a two step process – First create a thread and wake it up using wake_up_process(). However, kernel provides an API, which performs both these steps in one go as shown below:
#include <kthread.h> kthread_run(int (*function)(void *data), void *data, const char name[], ...)
Parameters:
function – The function that the thread has to execute
data – The ‘data’ to be passed to the function
name – The name by which the process will be recognized in the kernel
Returns: Pointer to a structure of type task_struct
So, just replace the kthread_create() and wake_up_process() calls in above code with kthread_run and you will notice that thread starts running immediately.
Conclusion
So, now we are comfortable with creating the threads, let us remove the module with rmmod. What do you get? Oops…isn’t it? To understand the reason for the crash, stay tuned to my next article on kernel threads. Till then, good bye.