Module with Two Kthread - semaphore
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 - semaphore");
Initialize all the required thread and semaphore variables.
static struct semaphore sema;
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);
sema_init(&sema);
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.
semaphore variable
sema
is initialized usingsema_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()) {
down(&sema);
if (!kfifo_is_empty(&my_fifo)) {
kfifo_out(&my_fifo, &value, sizeof(value));
pr_info("Thread 1 : Dequeue : %c\n", value);
val--;
}
up(&sema);
msleep(3000);
}
return 0;
}
int thread_func2(void *data)
{
char value = 'A';
char val = 'H';
while (val > 'A' && !kthread_should_stop()) {
down(&sema);
if (!kfifo_is_full(&my_fifo)) {
kfifo_in(&my_fifo, &value, sizeof(value));
pr_info("Thread 2 : Enqueue : %c\n", value);
value++;
val--;
}
up(&sema);
msleep(3000);
}
return 0;
}
In function thread_func1,
kfifo elements are removed from the kfifo with the help of semaphore.
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 semaphore.
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
14static struct semaphore sema;
15
16static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
17
18static struct task_struct *thread1;
19static struct task_struct *thread2;
20
21static int thread_func1(void *data);
22static int thread_func2(void *data);
23
24MODULE_LICENSE("GPL");
25MODULE_AUTHOR("Linux_usr");
26MODULE_DESCRIPTION("KFIFO - Semaphore");
27
28int thread_func1(void *data)
29{
30 char val = 'H';
31 char value;
32
33 while (!kthread_should_stop()) {
34 down(&sema);
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 up(&sema);
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 down(&sema);
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 up(&sema);
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 sema_init(&sema, 1);
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
86 return 0;
87}
88
89static void __exit thread_exit(void)
90{
91 if (thread1) {
92 kthread_stop(thread1);
93 thread1 = NULL;
94 }
95
96 if (thread2) {
97 kthread_stop(thread2);
98 thread2 = NULL;
99 }
100
101 pr_info("Char Device Driver Unloaded...\n");
102}
103
104module_init(thread_init);
105module_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
[ 169.117271] Kthread1 Created Successfully....
[ 169.117273] Thread 1 : FIFO is Empty
[ 169.117325] Kthread2 Created Successfully...
[ 169.117326] Char Device Driver Loaded...
[ 169.117328] Thread 2 : Enqueue : A
[ 172.175542] Thread 2 : Enqueue : B
[ 172.175548] Thread 1 : Dequeue : A
[ 175.247827] Thread 2 : Enqueue : C
[ 175.247833] Thread 1 : Dequeue : B
[ 178.320051] Thread 2 : Enqueue : D
[ 178.320054] Thread 1 : Dequeue : C
[ 181.392303] Thread 2 : Enqueue : E
[ 181.392309] Thread 1 : Dequeue : D
[ 184.464492] Thread 1 : Dequeue : E
[ 184.468408] Thread 2 : Enqueue : F
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
[ 169.117271] Kthread1 Created Successfully....
[ 169.117273] Thread 1 : FIFO is Empty
[ 169.117325] Kthread2 Created Successfully...
[ 169.117326] Char Device Driver Loaded...
[ 169.117328] Thread 2 : Enqueue : A
[ 172.175542] Thread 2 : Enqueue : B
[ 172.175548] Thread 1 : Dequeue : A
[ 175.247827] Thread 2 : Enqueue : C
[ 175.247833] Thread 1 : Dequeue : B
[ 178.320051] Thread 2 : Enqueue : D
[ 178.320054] Thread 1 : Dequeue : C
[ 181.392303] Thread 2 : Enqueue : E
[ 181.392309] Thread 1 : Dequeue : D
[ 184.464492] Thread 1 : Dequeue : E
[ 184.468408] Thread 2 : Enqueue : F
[ 187.472832] Char Device Driver Unloaded...
We can also get the information about the module using
modinfo
command.
$ modinfo threads.ko
filename: threads.ko
description: KFIFO - semaphore
author: Linux_usr
license: GPL
srcversion: 4194D2064F3CF66BCC7FA6A
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 |
sema_init |
Initialize semaphore |
down |
decrements semaphore variable |
up |
increments semaphore variable |
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 |