Skip to content

Task 1: Developing a Kernel Module

A kernel module is a program that can be dynamically loaded and unloaded into the kernel on demand. This allows for the extension of kernel functionality without requiring the rebuilding of the kernel or system reboot.

Numerous functionalities in the Linux kernel are implemented as modules. For instance, device drivers are commonly developed as modules. This modular approach enables the kernel to load only the necessary modules based on the hardware present in the system, effectively maintaining a compact kernel.

In this task, we will explore some basic concepts of kernel module development and play with some essential APIs for kernel development.

Say Hello from the Kernel

Create a directory for your "Hello World" module.

In the directory, create a file named "hello.c" with the following code to implement the "Hello World" module.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("cs5250");
MODULE_DESCRIPTION("A sample kernel module");

static int __init hello_init(void)
{
    printk(KERN_NOTICE "Hello World!\n");
    pr_info("Hello, world!\n");
    pr_debug("Greetings from %s.\n", THIS_MODULE->name);

    return 0;
}

static void __exit hello_exit(void)
{
    pr_info("Goodbye world.\n");
}

module_init(hello_init);
module_exit(hello_exit);

Kernel modules must include at least two functions: an initialization function that is called when the module is loaded into the kernel and a cleanup function that is invoked just before the module is removed from the kernel. These functions are registered using the module_init and module_exit macros. Please locate the accurate definition (as used here) of the two macros within the kernel source code.

Upon loading the module, the hello_init function is executed, and within it, we print messages using printk. Typically, these messages include a priority level like KERN_NOTICE, KERN_INFO or KERN_DEBUG. In modern conventions, it is recommended to use print macros such as pr_info and pr_debug as alternatives to directly employing printk. These macros wrap around printk, keeping things the same while making code simpler and look nicer by cutting down on repetitive typing.

To build the module, you need a Makefile. Please create a file named "Makefile" in the same directory as "hello.c" with the following content.

obj-m += hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Then run make to build the module. You should see a new file named "hello.ko" in the directory.

To load the module, use the insmod hello.ko command. After loading the module, you can check the kernel log using the dmesg command to see the "Hello World" message. Then you can unload the module using the rmmod hello command.

Did you see the output "Greetings from xxx" when you loaded the module? If not, is this string compiled into the module? How to make the message at the "DEBUG" level appear in the kernel log?

Passing Arguments to a Module

You can pass arguments to a module during its loading process by utilizing the module_param macro for declaring module parameters. Please search for further details on the usage of module_param, and modify the provided program below to declare two module parameters:

  • A string parameter named "tag" with a default value corresponding to your Matriculation Number.
  • An integer parameter named "pid" with a default value of 1.
// sudo insmod hello.ko
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/sched.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("cs5250");
MODULE_DESCRIPTION("A sample kernel module");

/* MODIFY THE CODE BELOW TO declare module parameters */
const char *tag = "A01234567A";
pid_t pid       = 1;
/* MODIFY THE CODE ABOVE TO declare module parameters */

static int __init hello_init(void)
{
    pr_debug("This is %s speaking.\n", tag);

    struct task_struct *task = get_pid_task(find_vpid(pid), PIDTYPE_PID);
    pr_info("%s: [%d] %s\n", tag, task->pid, task->comm);

    pr_debug("Goodbye from %s.\n", tag);
    return 0;
}

static void __exit hello_exit(void)
{
    pr_info("Goodbye world.\n");
}

module_init(hello_init);
module_exit(hello_exit);

Build the module and load it using the insmod command. You should observe an output in the kernel log similar to the following:

A01234567A: [1] systemd

Provide the insmod command with appropriate arguments to set the "tag" to cs5250 and the "pid" to the PID of any valid process. The module should then print the tag, PID, and the executable name of the process in a similar format.

Please modify the first line of your source code to indicate the command used for loading the module with the proper parameters.