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 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/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/kfifo.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("KFIFO - Spinlock");
Initialize all the required thread and spinlock variables.
spinlock_t sp;
static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
static struct task_struct *thread1;
static struct task_struct *thread2;
static int thread_func1(void *data);
static int thread_func2(void *data);
DECLARE_KFIFO
is a macro used to declare a kfifo and the associated buffer.
module-init function contains the instructions to create the thread and execute the thread via the corresponding thread function.
static int __init thread_init(void)
{
pr_info("Driver Loaded...\n");
INIT_KFIFO(my_fifo);
spin_lock_init(&sp);
thread1 = kthread_run(thread_func1, NULL, "thread1");
if (IS_ERR(thread1)) {
pr_alert("Failed to create thread1\n");
return PTR_ERR(thread1);
}
pr_info("Kthread1 Created Successfully....\n");
thread2 = kthread_run(thread_func2, NULL, "thread2");
if (IS_ERR(thread2)) {
pr_alert("Failed to create device\n");
return PTR_ERR(thread2);
}
pr_info("Kthread2 Created Successfully...\n");
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.
spinlock variable
sp
is initialized usingspin_lock_init
kfifo variable
myfifo
is initialized usingINIT_KFIFO
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 thread_exit(void)
{
if (thread1) {
kthread_stop(thread1);
thread1 = NULL;
}
if (thread2) {
kthread_stop(thread2);
thread2 = NULL;
}
pr_info("Driver Unloaded...\n");
}
thread function will execute a print statement until the thread is stopped and until that the thread will execute.
int thread_func1(void *data)
{
char val = 'H';
char value;
while (!kthread_should_stop()) {
spin_lock_bh(&sp);
if (!kfifo_is_empty(&my_fifo)) {
kfifo_out(&my_fifo, &value, sizeof(value));
pr_info("Thread 1 : Dequeue : %c\n", value);
val--;
}
spin_unlock_bh(&sp);
msleep(3000);
}
return 0;
}
int thread_func2(void *data)
{
char value = 'A';
char val = 'H';
while (val > 'A' && !kthread_should_stop()) {
spin_lock_bh(&sp);
if (!kfifo_is_full(&my_fifo)) {
kfifo_in(&my_fifo, &value, sizeof(value));
pr_info("Thread 2 : Enqueue : %c\n", value);
value++;
val--;
}
spin_unlock_bh(&sp);
msleep(3000);
}
return 0;
}
In function thread_func1,
kfifo elements are removed from the kfifo with the help of spinlock.
kfifo_is_empty
is used to check if the kfifo is empty or not.kfifo_out
is used to remove the element (dequeue) from kfifo.
In function thread_func2,
kfifo elements are inserted to kfifo with the help of spinlock.
kfifo_is_full
is used to check if the kfifo is full or not.kfifo_in
is used to insert the element (enqueue) to kfifo.
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/fs.h>
5#include <linux/uaccess.h>
6#include <linux/cdev.h>
7#include <linux/device.h>
8#include <linux/kthread.h>
9#include <linux/delay.h>
10#include <linux/kfifo.h>
11
12#define FIFO_SIZE 32
13
14MODULE_LICENSE("GPL");
15MODULE_AUTHOR("Linux_usr");
16MODULE_DESCRIPTION("KFIFO - Spinlock");
17
18spinlock_t sp;
19
20static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
21
22static struct task_struct *thread1;
23static struct task_struct *thread2;
24
25static int thread_func1(void *data);
26static int thread_func2(void *data);
27
28int thread_func1(void *data)
29{
30 char val = 'H';
31 char value;
32
33 while (!kthread_should_stop()) {
34 spin_lock_bh(&sp);
35 if (!kfifo_is_empty(&my_fifo)) {
36 kfifo_out(&my_fifo, &value, sizeof(value));
37 pr_info("Thread 1 : Dequeue : %c\n", value);
38 val--;
39 }
40 spin_unlock_bh(&sp);
41 msleep(3000);
42 }
43 return 0;
44}
45
46int thread_func2(void *data)
47{
48 char value = 'A';
49 char val = 'H';
50
51 while (val > 'A' && !kthread_should_stop()) {
52 spin_lock_bh(&sp);
53 if (!kfifo_is_full(&my_fifo)) {
54 kfifo_in(&my_fifo, &value, sizeof(value));
55 pr_info("Thread 2 : Enqueue : %c\n", value);
56 value++;
57 val--;
58 }
59 spin_unlock_bh(&sp);
60 msleep(3000);
61 }
62 return 0;
63}
64
65static int __init thread_init(void)
66{
67 pr_info("Driver Loaded...\n");
68
69 INIT_KFIFO(my_fifo);
70 spin_lock_init(&sp);
71
72 thread1 = kthread_run(thread_func1, NULL, "thread1");
73 if (IS_ERR(thread1)) {
74 pr_alert("Failed to create thread1\n");
75 return PTR_ERR(thread1);
76 }
77 pr_info("Kthread1 Created Successfully....\n");
78
79 thread2 = kthread_run(thread_func2, NULL, "thread2");
80 if (IS_ERR(thread2)) {
81 pr_alert("Failed to create device\n");
82 return PTR_ERR(thread2);
83 }
84 pr_info("Kthread2 Created Successfully...\n");
85 return 0;
86}
87
88static void __exit thread_exit(void)
89{
90 if (thread1) {
91 kthread_stop(thread1);
92 thread1 = NULL;
93 }
94
95 if (thread2) {
96 kthread_stop(thread2);
97 thread2 = NULL;
98 }
99
100 pr_info("Driver Unloaded...\n");
101}
102
103module_init(thread_init);
104module_exit(thread_exit);
1obj-m += threads.o
2
3KDIR = /lib/modules/$(shell uname -r)/build
4
5
6all:
7 make -C $(KDIR) M=$(shell pwd) modules
8
9clean:
10 make -C $(KDIR) M=$(shell 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
[ 472.940667] Driver Loaded...
[ 472.940697] Kthread1 Created Successfully....
[ 472.940714] Kthread2 Created Successfully...
[ 472.940716] Thread 2 : Enqueue : A
[ 475.955143] Thread 1 : Dequeue : A
[ 475.955148] Thread 2 : Enqueue : B
[ 479.027210] Thread 1 : Dequeue : B
[ 479.027216] Thread 2 : Enqueue : C
[ 482.099291] Thread 2 : Enqueue : D
[ 482.099298] Thread 1 : Dequeue : C
[ 485.171337] Thread 1 : Dequeue : D
[ 485.171345] Thread 2 : Enqueue : E
[ 488.243423] Thread 1 : Dequeue : E
[ 488.243429] Thread 2 : Enqueue : F
[ 491.315460] Thread 1 : Dequeue : F
[ 491.315462] Thread 2 : Enqueue : G
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
[ 472.940667] Driver Loaded...
[ 472.940697] Kthread1 Created Successfully....
[ 472.940714] Kthread2 Created Successfully...
[ 472.940716] Thread 2 : Enqueue : A
[ 475.955143] Thread 1 : Dequeue : A
[ 475.955148] Thread 2 : Enqueue : B
[ 479.027210] Thread 1 : Dequeue : B
[ 479.027216] Thread 2 : Enqueue : C
[ 482.099291] Thread 2 : Enqueue : D
[ 482.099298] Thread 1 : Dequeue : C
[ 485.171337] Thread 1 : Dequeue : D
[ 485.171345] Thread 2 : Enqueue : E
[ 488.243423] Thread 1 : Dequeue : E
[ 488.243429] Thread 2 : Enqueue : F
[ 491.315460] Thread 1 : Dequeue : F
[ 491.315462] Thread 2 : Enqueue : G
[ 494.323692] Driver Unloaded...
We can also get the information about the module using
modinfo
command.
$ modinfo threads.ko
filename: threads.ko
description: KFIFO - Spinlock
author: Linux_usr
license: GPL
srcversion: 71580D1D74B1EEEB57712DE
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 |
spin_lock_bh |
Acquires given lock |
spin_unlock_bh |
Releases given lock |
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. |
kfree |
free previously allocated memory by kmalloc |
DECLARE_KFIFO |
To declare a kfifo variable |
kfifo_is_empty |
used to check if the kfifo is empty or not, Returns 1 if empty otherwise 0 |
kfifo_out |
to dequeue an element from kfifo |
kfifo_is_full |
used to check if it is full, returns 1 if full otherwise 0 |
kfifo_in |
to enqueue an element to kfifo |
INIT_KFIFO |
To initialize kfifo |