Module with Two Kthread - semaphore

  • In this program, you are going to learn

How to create two threads ?

  • 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>
#include <linux/semaphore.h>
  • Initialize all the required thread and spinlock variables.

static struct task_struct *thread1;
static struct task_struct *thread2;

static struct semaphore sema;

struct node {
    int data;
    struct list_head list;
};

LIST_HEAD(my_list);
  • LIST_HEAD is used to initialize a list head structure.

  • 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 - SLL - semaphore");
  • 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)
{
    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);

    sema_init(&sema, 1);

    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,

    • node1, node2 is initialized, allocated memory dynamically and added to the list.

    • thread1, thread2 is created and the corresponding thread APIs is passed as an argument.

    • sema_init is used to initialize the semaphore variable.

  • 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)
{
    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,

    • threads which are created in init function are being terminated.

    • each entry in the list are being freed.

  • 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()) {
            down(&sema);
            list_for_each_entry(new_node, &my_list, list) {
                    new_node->data++;
                    pr_info("Executing thread function 1 : %d\n", new_node->data);
                }
            up(&sema);
            msleep(5000);
        }
    return 0;
}

static int thread_fun2(void *data)
{
    struct node *new_node;

    while (!kthread_should_stop()) {
                down(&sema);
                list_for_each_entry(new_node, &my_list, list) {
                        new_node->data++;
                pr_info("Executing thread function 2 : %d\n", new_node->data);
        }
                up(&sema);
                msleep(5000);
        }
    return 0;
}
  • In this thread fun1 and thread fun2,

    • A new node is being created.

    • whenever a new node is added to the list the sempahore variable is decremented..

    • once the new node is added to the list the semaphore variable is incremented incidating that the memory resource can be accessed now.

  • init and exit function name should be added in the module_init and module_exit parameter.

module_init(thread_init);
module_exit(thread_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#include <linux/semaphore.h>
 14
 15MODULE_LICENSE("GPL");
 16MODULE_AUTHOR("Linux_usr");
 17MODULE_DESCRIPTION("Two kthreads - SLL - semaphore");
 18
 19static struct task_struct *thread1;
 20static struct task_struct *thread2;
 21
 22static struct semaphore sema;
 23
 24struct node {
 25	int data;
 26	struct list_head list;
 27};
 28
 29LIST_HEAD(my_list);
 30
 31static int thread_fun1(void *data)
 32{
 33	struct node *new_node;
 34
 35	while (!kthread_should_stop()) {
 36		down(&sema);
 37		list_for_each_entry(new_node, &my_list, list) {
 38			new_node->data++;
 39			pr_info("Executing thread function 1 : %d\n", new_node->data);
 40		}
 41		up(&sema);
 42		msleep(5000);
 43	}
 44	return 0;
 45}
 46
 47static int thread_fun2(void *data)
 48{
 49	struct node *new_node;
 50
 51	while (!kthread_should_stop()) {
 52		down(&sema);
 53		list_for_each_entry(new_node, &my_list, list) {
 54			new_node->data++;
 55			pr_info("Executing thread function 2 : %d\n", new_node->data);
 56		}
 57		up(&sema);
 58		msleep(5000);
 59	}
 60	return 0;
 61}
 62
 63static int __init thread_init(void)
 64{
 65	struct node *node1;
 66	struct node *node2;
 67
 68	pr_info("Driver Loaded...\n");
 69
 70	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
 71	node1->data = 1;
 72	INIT_LIST_HEAD(&node1->list);
 73	list_add_tail(&node1->list, &my_list);
 74	
 75	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
 76	node2->data = 2;
 77	INIT_LIST_HEAD(&node2->list);
 78	list_add_tail(&node2->list, &my_list);
 79	
 80	sema_init(&sema, 1);
 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	thread2 = kthread_run(thread_fun2, NULL, "thread2");
 89	if (IS_ERR(thread1)) {
 90		pr_alert("Failed to create thraed2");
 91		return PTR_ERR(thread2);
 92	}
 93	return 0;
 94}
 95
 96static void __exit thread_exit(void)
 97{
 98	struct node *curr_node, *next_node;
 99
100	kthread_stop(thread1);
101	kthread_stop(thread2);
102	list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
103		list_del(&curr_node->list);
104		kfree(curr_node);
105	}
106	pr_info("Driver Unloaded...\n");
107}
108
109module_init(thread_init);
110module_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 thread.ko is generated or not.

$ 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 to load the kernel module to linux kernel.

$ sudo insmod ./threads.ko
  • Check for kernel messages to verify if the module is loaded or not.

$ dmesg
[29544.382165] Driver Loaded...
[29544.382220] Executing thread function 1 : 2
[29544.382221] Executing thread function 1 : 3
[29544.382249] Executing thread function 2 : 3
[29544.382249] Executing thread function 2 : 4
[29549.412286] Executing thread function 1 : 4
[29549.412290] Executing thread function 1 : 5
[29549.412314] Executing thread function 2 : 5
[29549.412317] Executing thread function 2 : 6
[29554.532362] Executing thread function 1 : 6
[29554.532364] Executing thread function 1 : 7
[29554.532384] Executing thread function 2 : 7
[29554.532387] Executing thread function 2 : 8
[29559.652494] Executing thread function 2 : 8
[29559.652500] Executing thread function 2 : 9
  • Run lsmod to check for the kernel module.

$ lsmod | grep threads
threads                16384  0
  • Unload the module using rmmod command.

$ sudo rmmod threads
  • Check kernel messages to see if the module is unloaded or not.

$ dmesg
[29544.382165] Driver Loaded...
[29544.382220] Executing thread function 1 : 2
[29544.382221] Executing thread function 1 : 3
[29544.382249] Executing thread function 2 : 3
[29544.382249] Executing thread function 2 : 4
[29549.412286] Executing thread function 1 : 4
[29549.412290] Executing thread function 1 : 5
[29549.412314] Executing thread function 2 : 5
[29549.412317] Executing thread function 2 : 6
[29554.532362] Executing thread function 1 : 6
[29554.532364] Executing thread function 1 : 7
[29554.532384] Executing thread function 2 : 7
[29554.532387] Executing thread function 2 : 8
[29559.652494] Executing thread function 2 : 8
[29559.652500] Executing thread function 2 : 9
[29564.772662] Driver Unloaded...
  • We can also get the information about the module using modinfo command.

$ modinfo threads.ko
filename:       threads.ko
description:    Two kthreads - SLL - semaphore
author:         Linux_usr
license:        GPL
srcversion:     C95E69F44323CF01B71ACC0
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

To declare a semaphore variable

LIST_HEAD

A list is headed by a structure defined by the LIST_HEAD macro

down

Decrements the semaphore variable

list_for_each_entry

iterate over list of given type

up

Increments the semaphore variable

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