LKM writing syntax
In this article, let’s write our very first kernel module, as shown in Figure 1. Here is an example of a hello world kernel module.
It looks like a normal user-space C program. But, we have to understand a lot of things here because we write kernel modules according to some rules because the kernel module is going to run in the kernel space.
Understanding the Structure
A kernel module can be divided into distinct sections:
There is a header section, there is your code section, and there is a registration section, and there is a module description section as shown in Figure 2. These sections are part of a kernel module. Let’s go through this section one by one.
Header Section:
Here, you have to mention the appropriate kernel header for your kernel module.
You can find all the kernel headers in the kernel source tree in the path /include/linux. That’s a place where all the kernel headers are placed. In that location, you find a kernel header called module.h.
Every kernel module should include this header file. Because it has got a definition of various macros, which we are going to use while writing a kernel module.
Kernel Headers vs. User-Space Headers
For example, what you just saw above is a kernel header. An example for a user-level header may be stdio.h. That’s a header file that you find in the standard C library. That’s a user level header. This is a kernel header.
Since you write a kernel module that is going to be executed in kernel space, you should be using kernel headers, never include any user-space library headers like C standard library header files. No userspace library is linked to the kernel module during a kernel build procedure. For example, you don’t link your kernel module code with a C standard library.
That’s why you should not use any user space headers while writing the kernel module. Most of the relevant kernel headers are placed in the linux source tree at the path /include/linux.
Code Section:
The code section comprises a collection of functions. This is a normal C function. But it looks weird because it has some kernel-related syntaxes that are very easy to understand; it does look like a normal C function.
Basically, while writing a kernel module, we use 2 entry points.
Module Initialization Function: This function initializes the module and is invoked during module insertion or system boot for statically linked modules. It returns an integer: 0 for success and non-zero for failure, preventing the module from loading.
Module Cleanup Function: This function is responsible for module cleanup tasks. It is called during module removal.
Module Initialization Function
Let’s understand more about the module initialization function(as shown in Figure 5); why it is required?
int fun_name(void); is the prototype of a module initialization function. It should return int and function name. You can give anything you want your custom name, and it will not be taking any parameters. Input parameters are void. The module initialization function must return a value; 0 for success, non zero means module initialization failed. So, the module will not get loaded into the kernel. This is an entry point to your module (like main). We talk in terms of entry points. This function will get called during boot time in the case of static modules.
In the case of dynamic modules, this function will get called during module insertion. There should be one module initialization entry point in the module.
For example, if you compile this kernel module as a dynamic module, this function will get called when you insert the module into the kernel, using user-level programs such as insmod.
You do some initialization of devices, Initialization of device private data structures, you request some memory dynamically for various kernel data structures and services, you request for allocation of major and minor numbers, device file creation, various things you do in this function.
All these things that we are going to understand when we write a character driver in the upcoming articles.
Basically, you do initialization-related things in this function. Because, it’s an entry point. It’s like a main.
‘static’ Keyword and ‘_init’ Tag
You have got a static keyword there and you have got to __init tag there.
In LKM development, you might notice the use of the static
keyword and the __init
tag. These serve the purpose of making the module initialization function module-specific and not callable from other parts of the kernel.
- Static Keyword: While it’s optional, using
static
is a good practice to indicate that the function is private to the module and should not provide services or functionalities required by other modules. - __init Tag: The
__init
tag signifies that this code is specific to module initialization and can be discarded from memory after module initialization is complete, saving valuable kernel memory. This tag is particularly useful for reducing kernel memory usage in dynamically loaded modules.
In summary, writing LKMs requires adherence to specific conventions and understanding the distinctions between user space and kernel space, as well as the use of appropriate headers and tags to ensure proper module behavior within the kernel environment.
Get the Full Course on Linux Device Driver Here
FastBit Embedded Brain Academy Courses
Click here: https://fastbitlab.com/course1