Module with Two Kthread - semaphore

  • In this program, you are going to learn

How to create two threads ?

  • 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 using sema_init

    • kfifo variable myfifo is initialized using INIT_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