Module with Two Kthread - spinlock
In this program, you are going to learn
How to create two threads ?
How to use below APIs ?
Here is the part by part explanation of the above kernel thread program.
This sections contains all the headers which contains the definition of the APIs used in the kernel thread program.
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/slab.h>
The module marco is initialized which lists out the license of the module, author of the module and description of the module.
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Two kthreads with Spinlock");
Initialize all the required thread and spinlock variables.
static struct task_struct *thread1;
static struct task_struct *thread2;
DEFINE_SPINLOCK(sp);
struct node {
int data;
struct list_head list;
};
LIST_HEAD(my_list);
LIST_HEAD
is used to initialize a list head structure.DEFINE_SPINLOCK
is used to declare a spinlock variable.
module-init function contains the instructions to create the thread and execute the thread via the corresponding thread function.
static int __init kthread_init(void)
{
struct node *node1;
struct node *node2;
pr_info("Driver Loaded...\n");
node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
node1->data = 1;
INIT_LIST_HEAD(&node1->list);
list_add_tail(&node1->list, &my_list);
node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
node2->data = 2;
INIT_LIST_HEAD(&node2->list);
list_add_tail(&node2->list, &my_list);
thread1 = kthread_run(thread_fun1, NULL, "thread1");
if (IS_ERR(thread1)) {
pr_alert("Failed to create thraed1");
return PTR_ERR(thread1);
}
thread2 = kthread_run(thread_fun2, NULL, "thread2");
if (IS_ERR(thread1)) {
pr_alert("Failed to create thraed2");
return PTR_ERR(thread2);
}
return 0;
}
In this init function,
two threads thread1,thread2 is created and the corresponding thread functions for both the threads are passed as arguments.
two nodes node1 and node2 is created.
INIT_LIST_HEAD
is used to initialize a list head for which memory is dynamically allocated.list_add_tail
is used to add a new entry before a specified head.
module-exit function is used to stop and clean up all the thread process going on from the start of module-init function.
static void __exit kthread_exit(void)
{
struct node *curr_node, *next_node;
kthread_stop(thread1);
kthread_stop(thread2);
list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
list_del(&curr_node->list);
kfree(curr_node);
}
pr_info("Device Driver Unloaded...\n");
}
In this exit function,
list_for_each_entry_safe
is used to iterate over the list.list_del
is used to delete the current node in the list.
thread function will execute a print statement until the thread is stopped and until that the thread will execute.
static int thread_fun1(void *data)
{
struct node *new_node;
while (!kthread_should_stop()) {
spin_lock(&sp);
list_for_each_entry(new_node, &my_list, list) {
new_node->data++;
pr_info("Executing thread function 1 : %d\n", new_node->data);
}
spin_unlock(&sp);
msleep(5000);
}
return 0;
}
static int thread_fun2(void *data)
{
struct node *new_node;
while (!kthread_should_stop()) {
spin_lock(&sp);
list_for_each_entry(new_node, &my_list, list) {
new_node->data++;
pr_info("Executing thread function 2 : %d\n", new_node->data);
}
spin_unlock(&sp);
msleep(5000);
}
return 0;
}
init and exit function name should be added in the module_init and module_exit parameter.
module_init(kthread_init);
module_exit(kthread_exit);
1#include <linux/init.h>
2#include <linux/kernel.h>
3#include <linux/module.h>
4#include <linux/cdev.h>
5#include <linux/device.h>
6#include <linux/fs.h>
7#include <linux/uaccess.h>
8#include <linux/kthread.h>
9#include <linux/delay.h>
10#include <linux/interrupt.h>
11#include <linux/list.h>
12#include <linux/slab.h>
13
14MODULE_LICENSE("GPL");
15MODULE_AUTHOR("Linux_usr");
16MODULE_DESCRIPTION("Two kthreads with Spinlock");
17
18static struct task_struct *thread1;
19static struct task_struct *thread2;
20
21DEFINE_SPINLOCK(sp);
22
23struct node {
24 int data;
25 struct list_head list;
26};
27
28LIST_HEAD(my_list);
29
30static int thread_fun1(void *data)
31{
32 struct node *new_node;
33
34 while (!kthread_should_stop()) {
35 spin_lock(&sp);
36 list_for_each_entry(new_node, &my_list, list) {
37 new_node->data++;
38 pr_info("Executing thread function 1 : %d\n", new_node->data);
39 }
40 spin_unlock(&sp);
41 msleep(5000);
42 }
43 return 0;
44}
45
46static int thread_fun2(void *data)
47{
48 struct node *new_node;
49
50 while (!kthread_should_stop()) {
51 spin_lock(&sp);
52 list_for_each_entry(new_node, &my_list, list) {
53 new_node->data++;
54 pr_info("Executing thread function 2 : %d\n", new_node->data);
55 }
56 spin_unlock(&sp);
57 msleep(5000);
58 }
59 return 0;
60}
61
62
63static int __init kthread_init(void)
64{
65 struct node *node1;
66 struct node *node2;
67
68 pr_info("Driver Loaded...\n");
69
70 node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
71 node1->data = 1;
72 INIT_LIST_HEAD(&node1->list);
73 list_add_tail(&node1->list, &my_list);
74
75 node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
76 node2->data = 2;
77 INIT_LIST_HEAD(&node2->list);
78 list_add_tail(&node2->list, &my_list);
79
80 thread1 = kthread_run(thread_fun1, NULL, "thread1");
81 if (IS_ERR(thread1)) {
82 pr_alert("Failed to create thraed1");
83 return PTR_ERR(thread1);
84 }
85
86 thread2 = kthread_run(thread_fun2, NULL, "thread2");
87 if (IS_ERR(thread1)) {
88 pr_alert("Failed to create thraed2");
89 return PTR_ERR(thread2);
90 }
91 return 0;
92}
93
94
95static void __exit kthread_exit(void)
96{
97 struct node *curr_node, *next_node;
98
99 kthread_stop(thread1);
100 kthread_stop(thread2);
101 list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
102 list_del(&curr_node->list);
103 kfree(curr_node);
104 }
105 pr_info("Device Driver Unloaded...\n");
106}
107
108module_init(kthread_init);
109module_exit(kthread_exit);
1obj-m += threads.o
2all:
3 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Run make to compile the kernel module.
$ make
make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_thread modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
CC [M] $HOME/kernel_thread/thread.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] $HOME/kernel_thread/thread.mod.o
LD [M] $HOME/kernel_thread/thread.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
Check if threads.ko is generated or not using ls command.
$ ls -l
total 408
-rwxrwxrwx 1 test test 1388 Sep 12 16:13 threads.c
-rw-rw-r-- 1 test test 194592 Sep 12 16:14 threads.ko
-rw-rw-r-- 1 test test 142 Sep 12 16:14 threads.mod
-rw-rw-r-- 1 test test 1066 Sep 12 16:14 threads.mod.c
-rw-rw-r-- 1 test test 93872 Sep 12 16:14 threads.mod.o
-rw-rw-r-- 1 test test 102136 Sep 12 16:14 threads.o
-rwxrwxrwx 1 test test 182 Sep 12 16:08 Makefile
-rw-rw-r-- 1 test test 142 Sep 12 16:14 modules.order
-rw-rw-r-- 1 test test 0 Sep 12 16:14 Module.symvers
-rwxrwxrwx 1 test test 2144 Sep 12 16:12 README
Run insmod command to load the kernel module in linux module.
sudo insmod ./threads.ko
Run dmesg to check the kernel message and verify if the module is loaded or not.
$ dmesg
[30470.237516] Driver Loaded...
[30470.237552] Executing thread function 1 : 2
[30470.237553] Executing thread function 1 : 3
[30470.237584] Executing thread function 2 : 3
[30470.237585] Executing thread function 2 : 4
[30475.383116] Executing thread function 2 : 4
[30475.383119] Executing thread function 2 : 5
[30475.383125] Executing thread function 1 : 5
[30475.383133] Executing thread function 1 : 6
[30480.503222] Executing thread function 2 : 6
[30480.503225] Executing thread function 2 : 7
[30480.503229] Executing thread function 1 : 7
[30480.503231] Executing thread function 1 : 8
Check lsmod output to find whether the threads kernel module is loaded or not.
$ lsmod | grep threads
threads 16384 0
Run rmmod command to unload the kernel module from linux kernel.
$ sudo rmmod threads
Check the kernel messages to see if the module is unloaded or not.
$ dmesg
[30470.237516] Driver Loaded...
[30470.237552] Executing thread function 1 : 2
[30470.237553] Executing thread function 1 : 3
[30470.237584] Executing thread function 2 : 3
[30470.237585] Executing thread function 2 : 4
[30475.383116] Executing thread function 2 : 4
[30475.383119] Executing thread function 2 : 5
[30475.383125] Executing thread function 1 : 5
[30475.383133] Executing thread function 1 : 6
[30480.503222] Executing thread function 2 : 6
[30480.503225] Executing thread function 2 : 7
[30480.503229] Executing thread function 1 : 7
[30480.503231] Executing thread function 1 : 8
[30485.511443] Device Driver Unloaded...
We can also get the information about the module using
modinfo
command.
$ modinfo threads.ko
filename: threads.ko
description: Two kthreads with Spinlock
author: Linux_usr
license: GPL
srcversion: DAE1A9A3D4570B634E91349
depends:
retpoline: Y
name: threads
vermagic: 6.2.0-32-generic SMP preempt mod_unload modversions
# |
Learning |
---|---|
MODULE_DESCRIPTION |
used to describe what the module does |
DEFINE_SPINLOCK |
To declare a spinlock variable and initialize it in one line |
LIST_HEAD |
A list is headed by a structure defined by the LIST_HEAD macro |
spin_lock |
Acquires given lock |
list_for_each_entry |
iterate over list of given type |
spin_unlock |
Releases given lock |
INIT_LIST_HEAD |
Initialize a list_head structure |
list_add_tail |
add a new entry |
kmalloc |
kernel memory allocation function |
kthread_run |
create and wake a thread |
IS_ERR |
used to check, Returns non-0 value if the ptr is an error, Otherwise 0 if it’s not an error |
pr_alert |
Print an alert-level message |
PTR_ERR |
used to print, Current value of the pointer |
kthread_stop |
stop a thread created by kthread_create. |
list_for_each_entry_safe |
iterate over list of given type safe against removal of list entry |
list_del |
deletes entry from list |
kfree |
free previously allocated memory by kmalloc |