Module with Two Kthread - mutex

  • 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/mutex.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 - mutex");
  • Initialize all the required thread and mutex variables.

struct mutex my_mutex;

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);
    mutex_init(&my_mutex);

    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.

    • mutex variable my_mutex is initialized using mutex_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()) {
        mutex_lock(&my_mutex);
        if (!kfifo_is_empty(&my_fifo)) {
            kfifo_out(&my_fifo, &value, sizeof(value));
            pr_info("Thread 1 : Dequeue : %c\n", value);
            val--;
        }
        mutex_unlock(&my_mutex);
        msleep(3000);
    }
    return 0;
}

int thread_func2(void *data)
{
    char value = 'A';
    char val = 'H';

    while (val > 'A' && !kthread_should_stop()) {
        mutex_lock(&my_mutex);
        if (!kfifo_is_full(&my_fifo)) {
            kfifo_in(&my_fifo, &value, sizeof(value));
            pr_info("Thread 2 : Enqueue : %c\n", value);
            value++;
            val--;
        }
        mutex_unlock(&my_mutex);
        msleep(3000);
    }
    return 0;
}
  • In function thread_func1,

    • kfifo elements are removed from the kfifo with the help of mutex.

    • 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 mutex.

    • 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/mutex.h>
  9#include <linux/kthread.h>
 10#include <linux/delay.h>
 11#include <linux/kfifo.h>
 12
 13#define FIFO_SIZE 32
 14
 15MODULE_LICENSE("GPL");
 16MODULE_AUTHOR("Linux_usr");
 17MODULE_DESCRIPTION("KFIFO - mutex");
 18
 19struct mutex my_mutex;
 20
 21static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
 22
 23static struct task_struct *thread1;
 24static struct task_struct *thread2;
 25
 26static int thread_func1(void *data);
 27static int thread_func2(void *data);
 28
 29int thread_func1(void *data)
 30{
 31	char val = 'H';
 32	char value;
 33
 34	while (!kthread_should_stop()) {
 35		mutex_lock(&my_mutex);
 36		if (!kfifo_is_empty(&my_fifo)) {
 37			kfifo_out(&my_fifo, &value, sizeof(value));
 38			pr_info("Thread 1 : Dequeue : %c\n", value);
 39			val--;
 40		} else {
 41			pr_info("Thread 1 : FIFO is Empty\n");
 42		}
 43		mutex_unlock(&my_mutex);
 44		msleep(3000);
 45	}
 46	return 0;
 47}
 48
 49int thread_func2(void *data)
 50{
 51	char value = 'A';
 52	char val = 'H';
 53
 54	while (val > 'A' && !kthread_should_stop()) {
 55		mutex_lock(&my_mutex);
 56		if (!kfifo_is_full(&my_fifo)) {
 57			kfifo_in(&my_fifo, &value, sizeof(value));
 58			pr_info("Thread 2 : Enqueue : %c\n", value);
 59			value++;
 60			val--;
 61		}
 62		mutex_unlock(&my_mutex);
 63		msleep(3000);
 64	}
 65	return 0;
 66}
 67
 68
 69static int __init thread_init(void)
 70{
 71
 72	INIT_KFIFO(my_fifo);
 73	mutex_init(&my_mutex);
 74
 75	thread1 = kthread_run(thread_func1, NULL, "thread1");
 76	if (IS_ERR(thread1)) {
 77		pr_alert("Failed to create thread1\n");
 78		return PTR_ERR(thread1);
 79	}
 80	pr_info("Kthread1 Created Successfully....\n");
 81
 82	thread2 = kthread_run(thread_func2, NULL, "thread2");
 83	if (IS_ERR(thread2)) {
 84		pr_alert("Failed to create device\n");
 85		return PTR_ERR(thread2);
 86	}
 87	pr_info("Kthread2 Created Successfully...\n");
 88	pr_info("Char Device Driver Loaded...\n");
 89
 90	return 0;
 91}
 92
 93static void __exit thread_exit(void)
 94{
 95	kthread_stop(thread1);
 96	kthread_stop(thread2);
 97	pr_info("Char Device Driver Unloaded...\n");
 98}
 99
100module_init(thread_init);
101module_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 - mutex
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

mutex_init

Initialize mutex

mutex_lock

Acquires given lock

mutex_unlock

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