Description
In this project, you will learn how to create a kernel module and load it into the Linux kernel. The project can be completed using the Ubuntu Linux running on a virtual machine. Although you may use an editor to write C programs, you will have to use the terminal application to compile the programs, and you will have to enter commands on the command line to manage the modules in the kernel. As you’ll discover, the advantage of developing kernel modules is that it is a relatively easy method of interacting with the kernel, thus allowing you to write programs that directly invoke kernel functions. It is important for you to keep in mind that you are indeed writing kernel code that directly interacts with the kernel. That normally means that any errors in the code could crash the system! However, since you will be using a virtual machine, any failures will at worst only require rebooting the system.
Part I—Creating a Simple Kernel Module The first part of this project involves following a series of steps for creating and inserting a module into the Linux kernel.
I.1. An Illustrative Program for a Simple Kernel Module /* This function is called when the module is loaded. */ int simple_init(void) { printk(KERN INFO “Loading Module\n”); return 0; } /* This function is called when the module is removed. */ void simple_exit(void) { printk(KERN INFO “Removing Module\n”); } /* Macros for registering module entry and exit points. */ module init(simple_init); module exit(simple_exit); MODULE LICENSE(“GPL”); MODULE DESCRIPTION(“Simple Module”); MODULE AUTHOR(“SGG”); – 2 – – 3 – Part II—Creating a Kernel Module Using Kernel Data Structures The second part of this project involves modifying the kernel module so that it uses the kernel linked-list data structure. We explore using the circular, doubly linked list that is available to kernel developers. Much of what we discuss is available in the Linux source code— in this instance, the include file II.1. A Kernel struct and its Description Notice the member struct list_head list. The list_head structure is defined in the include file II.2. Inserting Elements into the Kernel Linked List studentDemo = kmalloc(sizeof(*studentDemo), GFP KERNEL); studentDemo->studentNumber = 760120495; studentDemo->courseCredit = 3; studentDemo->grade = 5; INIT_LIST_HEAD(&studentDemo->list); – 4 – II.3. Traversing the Kernel Linked List list_for_each_entry(ptr, &student_list, list) { /* on each iteration ptr points */ /* to the next student struct */ } II.4. Removing Elements from the Kernel Linked List Careful memory management—which includes releasing memory to prevent memory leaks—is crucial when developing kernel-level code. Before your program terminates, it must remove all list elements (nodes). list for_each_entry_safe(ptr, next, &student_list, list) { /* on each iteration ptr points */ /* to the next student struct */ – 5 – Notice that after deleting each element, we return memory that was previously allocated with kmalloc() back to the kernel with the call to kfree(). Make sure that you print contents of each list element before removing it (this will play the role of confirming—to some extent—of the removal). Part III—Implement Simple Kernel Module Using Illustrative Program Use the code from Section I.1 to create the kernel module and to load and unload the module (proceed through the steps described above). Part IV— Implement Linked List Kernel Module Part V—Hints H2) Updating Package Lists and Installing the Linux Kernel Source in Ubuntu Download the package lists from the repositories and “update” them to get information on the newest versions of packages and their dependencies [1]: sudo apt-get update – 6 – Fetch new versions of packages existing on the machine if APT knows about these new versions by way of apt-get update, and handle intelligently the dependencies, so it might remove obsolete packages or add new ones [1]: sudo apt-get dist-upgrade Install just the headers in Ubuntu [2]: sudo apt-get install linux-headers-$(uname -r) Install the entire Linux kernel source in Ubuntu [2]: sudo apt-get install linux-source References (cf. VI below): [1] https://askubuntu.com/questions/222348/what-does-sudo-apt-get-update-do [2] https://stackoverflow.com/questions/16919512/linux-module-h-no-such-file-or-directory H3) Makefiles for Linux Kernel Source: “The Linux Kernel Module Programming Guide” by Peter Jay Salzman, available at: https://tldp.org/LDP/lkmpg/2.6/html/lkmpg.html CRITICAL: Generally, ‘make’ is used to build a kernel module and not a bare cc. More on this: Kernel modules need to be compiled a bit differently from regular userspace apps. Former kernel versions required us to care much about these settings, which are usually stored in Makefiles. Although hierarchically organized, many redundant settings accumulated in sublevel Makefiles and made them large and rather difficult to maintain. Fortunately, there is a new way of doing these things, called kbuild, and the build process for external loadable modules is now fully integrated into the standard kernel build mechanism. To learn more on how to compile modules which are not part of the official kernel (such as all the examples you’ll find in this guide), see file linux/Documentation/kbuild/modules.txt.
Program code. The following program (named simple.c) is used to illustrate creating a very basic kernel module; this module prints two simple messages: one when the kernel module is loaded, and one when the kernel module is unloaded.
#include
Program explanation. The function simple_init() is the module entry point, which represents the function that is invoked when the module is loaded into the kernel. Similarly, the simple_exit() function is the module exit point—the function that is called when the module is removed from the kernel. The module entry point function must return an integer value, with 0 representing success and any other value representing failure. The module exit point function returns void. Neither the module entry point nor the module exit point is passed any parameters. The two following macros are used for registering the module entry and exit points with the kernel:
module_init() module_exit()
Notice how both the module entry and exit point functions make calls to the printk() function. printk() is the kernel equivalent of printf(), yet its output is sent to a kernel log buffer whose contents can be read by the dmesg command. One difference between printf() and printk() is that printk() allows us to specify a priority flag whose values are given in the
The final lines—MODULE_LICENSE(), MODULE_DESCRIPTION(), and MODULE_AUTHOR()—represent details regarding the software license, description of the module, and author. For our purposes, we do not depend on this information, but we include it because it is standard practice in developing kernel modules.
Compilation. This kernel module simple.c is compiled using the Makefile accompanying the source code with this project. To compile the module, enter the following on the command line:
make
The compilation produces several files. The file simple.ko represents the compiled kernel module. The following step illustrates inserting this module into the Linux kernel.
I.2. Loading and Removing Kernel Modules
Kernel modules are loaded using the insmod command, which is run as follows:
sudo insmod simple.ko
To check whether the module has loaded, enter the lsmod command and search for the module simple. Recall that the module entry point is invoked when the module is inserted into the kernel. To check the contents of this message in the kernel log buffer, enter the command
dmesg
You should see the message “Loading Module.”
Removing the kernel module involves invoking the rmmod command (notice that the .ko suffix is unnecessary):
sudo rmmod simple
Be sure to check with the dmesg command to ensure the module has been removed.
Because the kernel log buffer can fill up quickly, it often makes sense to clear the buffer periodically. This can be accomplished as follows:
sudo dmesg -c
Initially, you must define a struct containing the elements that are to be inserted in the linked list. The following C struct defines students:
struct student { int studentNumber; int courseCredit; int grade; struct list_head list; }
We can declare a list_head object, which we use as a reference to the head of the list by using the LIST_HEAD() macro
static LIST_HEAD(student_list);
This macro defines and initializes the variable student_list, which is of type struct list_head.
We create and initialize instances of struct student as follows:
struct student *studentDemo;
The kmalloc() function is the kernel equivalent of the user-level malloc() function for allocating memory, except that kernel memory is being allocated. (The GFP_KERNEL flag indicates routine kernel memory allocation.) The macro INIT_LIST_HEAD() initializes the list member in struct student. We can then add this instance to the end of the linked list using the list_add_tail() macro:
list_add_tail(&studentDemo->list, &student_list);
Traversing the list involves using the list_for_each_entry() macro (see list.h), which accepts three parameters: • A pointer to the structure being iterated over • A pointer to the head of the list being iterated over • The name of the variable containing the list_head structure The following code illustrates this macro:
struct student *ptr;
Removing elements from the list involves using the list_del() macro, which is passed a pointer to struct list_head
list_del(struct list_head *element)
This removes element from the list while maintaining the structure of the remainder of the list.
Perhaps the simplest approach for removing all elements from a linked list is to remove each element as you traverse the list. The macro list_for_each_entry_safe() behaves much like list_for_each_entry()except that it is passed an additional argument that maintains the value of the next pointer of the item being deleted. (This is necessary for preserving the structure of the list.) The following code example illustrates this macro:
struct student *ptr, *next
list_del(&ptr->list); kfree(ptr); }
Be sure to display the contents of the kernel log buffer (using dmesg) in your report for this assignment.
Entry point. In the module entry point, create a linked list containing six struct student elements for a single students, that is with the same value of studentNumber (and different values of courseCredit and grade). Traverse the linked list and output its contents to the kernel log buffer. Invoke the dmesg command to ensure the list is properly constructed once the kernel module has been loaded.
Exit point. In the module exit point, delete the elements from the linked list and return the free memory back to the kernel. Again, invoke the dmesg command to check that the list has been removed once the kernel module has been unloaded.
H1) You need to use #include
Note: APT, or Advanced Package Tool is a free software user interface that works with core libraries to handle the installation and removal of software on the Debian GNU/Linux distribution and its variants. [https://en.wikipedia.org/wiki/Advanced_Packaging_Tool]