Module with Two Kthread - spinlock_bh
In this program, you are going to learn
How to create two threads ?
How to use below APIs ?
Here is the explanation of thread program part by part
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>
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.
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_irqsave");
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("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_bh(&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_bh(&sp);
msleep(5000);
}
return 0;
}
static int thread_fun2(void *data)
{
struct node *new_node;
while (!kthread_should_stop()) {
spin_lock_bh(&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_bh(&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_irqsave");
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_bh(&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_bh(&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_bh(&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_bh(&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
71 node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
72 node1->data = 1;
73 INIT_LIST_HEAD(&node1->list);
74 list_add_tail(&node1->list, &my_list);
75
76 node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
77 node2->data = 2;
78 INIT_LIST_HEAD(&node2->list);
79 list_add_tail(&node2->list, &my_list);
80
81
82 thread1 = kthread_run(thread_fun1, NULL, "thread1");
83 if (IS_ERR(thread1)) {
84 pr_alert("Failed to create thraed1");
85 return PTR_ERR(thread1);
86 }
87
88
89 thread2 = kthread_run(thread_fun2, NULL, "thread2");
90 if (IS_ERR(thread1)) {
91 pr_alert("Failed to create thraed2");
92 return PTR_ERR(thread2);
93 }
94
95 return 0;
96}
97
98static void __exit kthread_exit(void)
99{
100 struct node *curr_node, *next_node;
101
102 kthread_stop(thread1);
103 kthread_stop(thread2);
104 list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
105 list_del(&curr_node->list);
106 kfree(curr_node);
107 }
108 pr_info("Driver Unloaded...\n");
109}
110
111module_init(kthread_init);
112module_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
[31047.420026] Driver Loaded...
[31047.420106] Executing thread function 1 : 2
[31047.420109] Executing thread function 1 : 3
[31047.420141] Executing thread function 2 : 3
[31047.420142] Executing thread function 2 : 4
[31052.674684] Executing thread function 2 : 4
[31052.674687] Executing thread function 2 : 5
[31052.674692] Executing thread function 1 : 5
[31052.674694] Executing thread function 1 : 6
[31057.794771] Executing thread function 1 : 6
[31057.794774] Executing thread function 1 : 7
[31057.794778] Executing thread function 2 : 7
[31057.794781] Executing thread function 2 : 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 in linux module.
sudo insmod ./threads.ko
Run dmesg to check the kernel message and verify if the module is unloaded or not.
$ dmesg
[31047.420026] Driver Loaded...
[31047.420106] Executing thread function 1 : 2
[31047.420109] Executing thread function 1 : 3
[31047.420141] Executing thread function 2 : 3
[31047.420142] Executing thread function 2 : 4
[31052.674684] Executing thread function 2 : 4
[31052.674687] Executing thread function 2 : 5
[31052.674692] Executing thread function 1 : 5
[31052.674694] Executing thread function 1 : 6
[31057.794771] Executing thread function 1 : 6
[31057.794774] Executing thread function 1 : 7
[31057.794778] Executing thread function 2 : 7
[31057.794781] Executing thread function 2 : 8
[31062.819070] 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_irqsave
author: Linux_usr
license: GPL
srcversion: 33A16D633CA13297F690405
depends:
retpoline: Y
name: threads
vermagic: 5.4.0-150-generic SMP 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_bh |
Acquires given lock |
list_for_each_entry |
iterate over list of given type |
spin_unlock_bh |
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 |