4. Kernel Modules¶
4.1. Introduction¶
We generally do not make changes directly to the core kernel code. We mostly write small modules to be added to the kernel. This way we can separate out our changes from the mainline kernel.
If you see - the file system are thus modules only, as not all the Linux installations do not need all the file systems and the user can load them on demand.
In this chapter we will see how to write kernel modules.
4.1.1. Why this chapter¶
This chapter is the most important building block of the whole document. So spend some time with it. Do not just copy paste the code.
Type the code and then run it. Read the comments and type them as well so as to understand what the comments mean.
The more time you spend here - the lesser time you will spend later.
4.1.2. What will you learn¶
We will learn
- How to write a basic kernel module.
- How to write functions and make it available to other modules. Sort of library.
- How to pass parameters to the modules which is getting loaded.
4.1.3. Concepts and Keywords involved¶
4.1.4. Prerequisites¶
C Programming.
4.2. What are Kernel Modules¶
- Pieces of code that can be loaded and unloaded into the kernel upon demand.
- Extends the functionality of the kernel without the need to reboot the system.
- Device drivers which allows the kernel to access hardware connected to the system. If we plug any new device the kernel cannot use it properly unless we have the right drivers in place. The kernel detects the device type and loads the respective modules from the code base. Thus device drivers are generally modules (apart from the basic / legacy drivers.)
4.3. Kernel Module Advantages¶
- Without modules, we would have to build monolithic kernels.
- Adding new functionality would require adding the code in the kernel image which will need recompilation.
- Monolithic kernel leads to larger kernel images resulting in loading code which is not required.
- Rebooting machine whenever new functionality is added.
- Example: till the time you don’t have to mount a Ext2 file system why will you load code for reading ext2 file system.
4.4. Tools for Kernel Modules¶
Before getting into writing a module we must first see the tools/commands which we will use to get the modules working.
4.4.1. insmod
¶
insmod
- insert module.
4.4.2. modprobe
¶
modprobe -r
- remove the module present in the kernel.
4.4.3. rmmod
¶
rmmod
- remove the module present in the kernel.
4.4.4. lsmod
¶
lsmod
- list the modules inserted in the kernel.
4.4.5. modinfo
¶
modinfo
- prints the information of the module.
4.4.6. dmesg
¶
dmesg
4.4.7. syslog
¶
syslog
4.4.8. Some System Calls¶
init_module(), query_module(), create_module(), delete_module()
- system calls called by the various commands to insert/delete module to the kernel space.
4.5. New Functions¶
There are few new things which you will see in the code.
4.5.1. printk()
¶
printk()
- this is the kernel counter part of the user spaceprintf()
function. Its syntax isprintk(LEVEL "FORMAT_SPECIFIER", parameters)
. TheLEVEL
is used to specify the severity of the message you wish to print. See different_log_levels_printk-label.
4.5.2. module_init()
¶
module_init()
-module_init()
is the first function which will be called when the module is loaded. This gives us a entry point to the kernel module. Here we can initialize the global variables, setup functions, setup enviornment and other stuff. The use of this will be clear in the next chapters.
4.5.3. module_exit()
¶
module_exit()
-module_exit()
is the function which is called when the module is being unloaded from the system. Here we can free the allocated memory, free the locks and do other cleanup actions. This will be clear in the coming chapters.
4.6. Hello World Kernel Module¶
4.6.1. Introduction¶
We will write our first kernel module. We will code it, compile it and then
insert
the module in the kernel. When the module gets inserted the kernel
gets the functionality provided by the module. In this Hello World
module,
their is no functionality provided to the kernel. The module just gets inserted
and it prints Hello World
. You can see the message printed by the kernel
module by running the dmesg
tool. dmesg
shows you the buffer of the
kernel.
4.6.2. Code¶
4.6.2.1. FILE: mymodule.c
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /*
* <PD> Hello World Module </PD>
*/
#include <linux/module.h>
#include <linux/init.h>
/*
* This is the starting point of the kernel module's code execution.
* Right now we will just print a hello and return from here.
*/
static int __init my_init(void)
{
printk(KERN_INFO "Hello from Hello Module");
return 0;
}
/*
* This is the exit point of the kernel module. When the module is removed
* this function is called to do any sort of cleanup activities and other such
* stuff.
*
* For example you have made a tree where you keep someinformation - you would
* like to place the code for removing the nodes of the tree here.
*/
static void __exit my_exit(void)
{
printk(KERN_INFO "Bye from Hello Module");
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("Sample Hello World Module");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");
|
4.6.2.2. FILE: Makefile
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | MODULE_FILENAME=mymodule
obj-m += $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko
export KROOT=/lib/modules/$(shell uname -r)/build
modules:
@$(MAKE) -C $(KROOT) M=$(PWD) modules
modules_install:
@$(MAKE) -C $(KROOT) M=$(PWD) modules_install
clean:
@$(MAKE) -C $(KROOT) M=$(PWD) clean
rm -rf Module.symvers modules.order
insert: modules
sudo insmod $(KO_FILE)
remove:
sudo rmmod $(MODULE_FILENAME)
printlog:
sudo dmesg -c
sudo insmod $(KO_FILE)
dmesg
|
4.6.3. Common Questions¶
4.6.3.1. How to run the code¶
To insert the module we need to use the insmod
command with the module
name. We need to be root
while doing it.
In the Makefile
we have a target insert
. This target calls the
insmod
command to insert the module into the kernel.
4.6.3.2. How the code gets executed¶
On inserting the module the function registered as module_init()
gets
called. In our case it is my_init()
.
This function just prints a message to the logs and returns.
4.6.3.3. How to remove the module¶
To remove the module we need to use the rmmod
command with the module name.
We need to be root
while doing it.
rmmod mymodule
4.6.3.4. How the module gets removed/unloaded¶
When the rmmod
command is invoked the function registered with
module_exit()
is called. In our case it is my_exit()
. The function just
prints the messages in the logs and then returns.
4.7. Module Which Does Some Calculations¶
4.7.1. Introduction¶
The above module did not do anything and just printed some messages. We will now write a module which will do some calculations.
The intention of this excercise it to show that its plain C code and you can do the regular stuff here as well.
4.7.2. Code¶
4.7.2.1. FILE: mycalc.c
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | /*
* <PD> Module to demostrate how to do some calculations. </PD>
*/
#include <linux/module.h>
#include <linux/init.h>
struct sample {
int x, y;
};
void print_sample_struct(struct sample temp) {
printk (KERN_INFO "\nx is %d y is %d", temp.x, temp.y);
}
void calculator(void) {
int a = 15, b = 3;
int *ptra = &a;
int i;
struct sample temp;
temp.x = 10;
temp.y = 20;
printk (KERN_INFO "\nSum is %d", a+b);
printk (KERN_INFO "\nDifference is %d", a-b);
printk (KERN_INFO "\nProduct is %d", a*b);
printk (KERN_INFO "\nDivison is %d", a/b);
printk (KERN_INFO "\nMod is %d", a%b);
printk (KERN_INFO "\nBitwise NOT of %d Is %d", a, ~a);
printk (KERN_INFO "\nBitwise OR is %d", a|b);
printk (KERN_INFO "\nBitwise AND is %d", a&b);
printk (KERN_INFO "\nBitwise XOR Is %d", a^b);
printk (KERN_INFO "\nLogical OR Is %d", a||b);
printk (KERN_INFO "\nLogical AND Is %d", a&&b);
if (a>b) {
printk (KERN_INFO "\n%d is greater than %d", a, b);
} else if (b>a) {
printk (KERN_INFO "\n%d is greater than %d", b, a);
} else {
printk (KERN_INFO "\n%d is equal to %d", b, a);
}
printk (KERN_INFO "\nAddress of a is %p", ptra);
printk (KERN_INFO "\nValue of ptra is %d", *ptra);
/*
* You can have loops as well.
*/
for (i = b; i <=a; i++) {
printk (KERN_INFO "\nPrinting i %d", i);
}
/*
* You can have structures as well.
*/
print_sample_struct(temp);
}
static int __init my_init(void)
{
printk(KERN_INFO "Hello from Hello Module");
calculator();
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "Bye from Hello Module");
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("Sample Hello World Module");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");
|
4.7.2.2. FILE: Makefile
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | MODULE_FILENAME=mycalc
obj-m += $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko
export KROOT=/lib/modules/$(shell uname -r)/build
compile:
@$(MAKE) -C $(KROOT) M=$(PWD) modules
modules_install:
@$(MAKE) -C $(KROOT) M=$(PWD) modules_install
clean:
@$(MAKE) -C $(KROOT) M=$(PWD) clean
rm -rf Module.symvers modules.order
insert: compile
sudo insmod $(KO_FILE)
remove:
sudo rmmod $(MODULE_FILENAME)
printlog:
sudo dmesg -c
sudo insmod $(KO_FILE)
dmesg
|
4.8. Module with Parameters¶
4.8.1. Introduction¶
In the above calculator if we want to change the variables, or we want to do calculation of different integers we will have to change the code and hardcode the value.
We do not want that, we want that the values should be taken from the user. For this we can pass the paramters to the kernel module to do it.
First we will see a small module which does it and then we will see it in the calculator program.
4.8.2. Code¶
4.8.2.1. FILE: mymodule_with_parameters.c
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /*
* <PD> Program to add the passed parameters to a kernel module </PD>
*/
#include <linux/module.h>
#include <linux/init.h>
#define DEFAULT_PARAM1 100
#define DEFAULT_PARAM2 200
/*
* Variable for integer parameter
*/
int param1 = DEFAULT_PARAM1;
int param2 = DEFAULT_PARAM2;
/*
* Get the parameters.
*/
module_param(param2, int, 0);
module_param(param1, int, 0);
static int __init my_init(void)
{
printk(KERN_INFO "\nHello !! from Paramter Passing Demo Module\n");
/*
* Print the parameters passed
*/
printk(KERN_INFO "\nThe sum of the parameters are :%d:", param1 + param2);
printk(KERN_INFO "\nPassed Parameters\n");
if (param1 == DEFAULT_PARAM1) {
printk(KERN_INFO "\nNothing Passed OR Default Value :%d: for param1 is Passed\n", DEFAULT_PARAM1);
} else {
printk(KERN_INFO "\nparam1 passed is :%d:", param1);
}
if (param1 == DEFAULT_PARAM2) {
printk(KERN_INFO "\nNothing Passed OR Default Value :%d: for param1 Passed\n", DEFAULT_PARAM2);
} else {
printk(KERN_INFO "\nparam2 passed is :%d:", param2);
}
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "\nBye from Parameter Passing Demo Module");
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("Module To Demonstrate Module Parameters");
MODULE_AUTHOR("rishi.b.agrawal@gmail.com");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
|
4.8.2.2. FILE: Makefile
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | MODULE_FILENAME=mymodule
obj-m += $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko
export KROOT=/lib/modules/$(shell uname -r)/build
compile: clean
@$(MAKE) -C $(KROOT) M=$(PWD) modules
modules_install:
@$(MAKE) -C $(KROOT) M=$(PWD) modules_install
clean:
@$(MAKE) -C $(KROOT) M=$(PWD) clean
rm -rf Module.symvers modules.order
insert: compile
sudo insmod $(KO_FILE) param1=10 param2=20
remove:
sudo rmmod $(MODULE_FILENAME)
printlog:
sudo dmesg -c
sudo insmod $(KO_FILE)
dmesg
output:
sudo rmmod $(MODULE_FILENAME)
sudo dmesg -c
sudo insmod $(KO_FILE) param1=10 param2=20
sudo dmesg -c
testsanity: clean compile insert remove
|
4.9. Module with Character Array and Arrays as parameters¶
4.9.1. Introduction¶
Passing paramters is not limited to integers only, we can pass characters and arrays as well. See the following example to understand how to do it.
4.9.2. Questions¶
How to pass floats?
4.9.3. Code¶
4.9.3.1. FILE: mymodule_with_parameters.c
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | /*
* <PD> Program to demonstrate arrays and strings for module paramters. </PD>
*/
#include <linux/module.h>
#include <linux/init.h>
#define DEFAULT_PARAM1 100
#define ARRAY_LEN 5
#define STRING_LEN 10
/*
* Variable for integer parameter
*/
int param1 = DEFAULT_PARAM1;
module_param(param1, int, S_IRUGO | S_IWUSR);
/*
* Variable for named parameters
*/
static int for_myself = 42;
module_param_named(for_world, for_myself, int, 0444);
MODULE_PARM_DESC(for_world, "For the world");
/*
* Variable for integer array
*/
static int int_array[ARRAY_LEN];
int array_len;
module_param_array(int_array, int, &array_len, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(int_array, "Integer array for doing nothing");
/*
* Variable for strings
*/
char test[STRING_LEN];
module_param_string(test_string, test, STRING_LEN, S_IRUGO | S_IWUSR);
static int __init my_init(void)
{
int i = 0;
printk(KERN_INFO "\nHello from Hello Module\n");
printk(KERN_INFO "\nPassed Parameters\n");
/*
* Print the parameters passed
*/
if (param1 == DEFAULT_PARAM1) {
printk(KERN_INFO
"\nNothing Passed or Default Value %d for param1 \
passed\n",
DEFAULT_PARAM1);
} else {
printk(KERN_INFO "\nParam1 passed is %d", param1);
}
/*
* Module Parameter named - see the file
* /sys/module/-module-name-/parameters
*/
printk(KERN_INFO "\nValue of for_myself is %d", for_myself);
/*
* Integer array as a parameter
*/
for (i = 0; i < array_len; i++) {
printk(KERN_INFO "Interger Array element %d is %d", i,
int_array[i]);
}
/*
Print the Character array
*/
printk(KERN_INFO "\nThe character array passed %s", test);
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "Bye from Hello Module");
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("module to demonstrate module parameters");
MODULE_AUTHOR("abr");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
|
4.9.3.2. FILE: Makefile
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | MODULE_FILENAME=mymodule_with_parameters
obj-m += $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko
export KROOT=/lib/modules/$(shell uname -r)/build
compile:
@$(MAKE) -C $(KROOT) M=$(PWD) modules
modules_install:
@$(MAKE) -C $(KROOT) M=$(PWD) modules_install
clean:
@$(MAKE) -C $(KROOT) M=$(PWD) clean
rm -rf Module.symvers modules.order
insert: compile
sudo insmod $(KO_FILE)
remove:
sudo rmmod $(MODULE_FILENAME)
printlog:
sudo dmesg -c
sudo insmod $(KO_FILE)
dmesg
|
4.10. Calculator with parameters¶
4.10.1. Introduction¶
In this module, we have mostly changed the calculator’s code to suite the parameter passing.
4.10.2. Code¶
4.10.2.1. FILE: mycalc_with_parameters.c
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | /*
* <PD> Calculator with parameters </PD>
*/
#include <linux/module.h>
#include <linux/init.h>
struct sample {
int x, y;
};
void print_sample_struct(struct sample temp) {
printk (KERN_INFO "\nx is %d y is %d", temp.x, temp.y);
}
/*
* Variable for integer parameter
*/
int param1;
module_param(param1, int, 0);
int param2;
module_param(param2, int, 0);
void calculator(int a, int b) {
int *ptra = &a;
int i;
struct sample temp;
temp.x = 10;
temp.y = 20;
printk (KERN_INFO "\nSum is %d", a+b);
printk (KERN_INFO "\nDifference is %d", a-b);
printk (KERN_INFO "\nProduct is %d", a*b);
printk (KERN_INFO "\nDivison is %d", a/b);
printk (KERN_INFO "\nMod is %d", a%b);
printk (KERN_INFO "\nBitwise NOT of %d Is %d", a, ~a);
printk (KERN_INFO "\nBitwise OR is %d", a|b);
printk (KERN_INFO "\nBitwise AND is %d", a&b);
printk (KERN_INFO "\nBitwise XOR Is %d", a^b);
printk (KERN_INFO "\nLogical OR Is %d", a||b);
printk (KERN_INFO "\nLogical AND Is %d", a&&b);
if (a>b) {
printk (KERN_INFO "\n%d is greater than %d", a, b);
} else if (b>a) {
printk (KERN_INFO "\n%d is greater than %d", b, a);
} else {
printk (KERN_INFO "\n%d is equal to %d", b, a);
}
printk (KERN_INFO "\nAddress of a is %p", ptra);
printk (KERN_INFO "\nValue of ptra is %d", *ptra);
/*
* You can have loops as well.
*/
for (i = b; i <=a; i++) {
printk (KERN_INFO "\nPrinting i %d", i);
}
/*
* You can have structures as well.
*/
print_sample_struct(temp);
}
/*
* This is the starting point of the kernel module's code execution.
* Right now we will just print a hello and return from here.
*/
static int __init my_init(void)
{
/*
* Variable for integer parameter
*/
printk(KERN_INFO "Hello from Hello Module");
calculator(param1, param2);
return 0;
}
/*
* This is the exit point of the kernel module. When the module is removed
* this function is called to do any sort of cleanup activities and other such
* stuff.
*
* For example you have made a tree where you keep someinformation - you would
* like to place the code for removing the nodes of the tree here.
*/
static void __exit my_exit(void)
{
printk(KERN_INFO "Bye from Hello Module");
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("Calculator with parameters.");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");
|
4.10.2.2. FILE: Makefile
¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | MODULE_FILENAME=mycalc_with_parameters
obj-m += $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko
export KROOT=/lib/modules/$(shell uname -r)/build
modules:
@$(MAKE) -C $(KROOT) M=$(PWD) modules
modules_install:
@$(MAKE) -C $(KROOT) M=$(PWD) modules_install
clean:
@$(MAKE) -C $(KROOT) M=$(PWD) clean
rm -rf Module.symvers modules.order
insert:
sudo insmod $(KO_FILE)
remove:
sudo rmmod $(MODULE_FILENAME)
printlog:
sudo dmesg -c
sudo insmod $(KO_FILE)
dmesg
|
4.11. Conclusion¶
In this chapter we mostly learnt about the very basics of kernel module programming. We have a lot of ground to cover. Let’s get into other concepts of modules.