《Operating-System-Concepts(9th)》
Project Report
Linux Kernel Modules
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 Linux virtual machine that is available with this text. Although you may use an editor to write
these 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 Kernel Modules
The first part of this project involves following a series of steps for creating and inserting a module into the Linux
kernel. You can list all kernel modules that are currently loaded by entering the command
lsmod
This command will list the current kernel modules in three columns: name, size, and where the module is being
used. The following program (named simple.c and available with the source code for this text) illustrates a very
basic kernel module that prints appropriate messages when the kernel module is loaded and unloaded.
#include
#include
#include
/* 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");
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 include file. In this instance, the priority is KERN INFO,
which is defined as an informational message.
The final lines—MODULE LICENSE(), MODULE DESCRIPTION(), and MODULEAUTHOR()—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. 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.
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 Part I Assignment Proceed through the steps described above to create the kernel module
and to load and unload the module. Be sure to check the contents of the kernel log buffer using dmesg to ensure
you have properly followed the steps.
Part II—Kernel Data Structures
The second part of this project involves modifying the kernel module so that it uses the kernel linked-list data
structure.
In Section 1.10, we covered various data structures that are common in operating systems. The Linux kernel
provides several of these structures. Here, 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 —and we recommend that you examine this file as you proceed through the following steps.
Initially, you must define a struct containing the elements that are to be inserted in the linked list. The following C
struct defines birthdays:
struct birthday {
int day;
int month;
int year;
struct list_head list;
}
Notice the member struct list head list. The list head structure is defined in the include file . Its
intention is to embed the linked list within the nodes that comprise the list. This list head structure is quite
simple—it merely holds two members, next and prev, that point to the next and previous entries in the list. By
embedding the linked list within the structure, Linux makes it possible to manage the data structure with a series
of macro functions.
Inserting Elements into the Linked 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(birthday_list);
This macro defines and initializes the variable birthday_list, which is of type struct list_head.
We create and initialize instances of struct birthday as follows:
struct birthday *person;
person = kmalloc(sizeof(*person), GFP KERNEL);
person->day = 2; person->month= 8;
person->year = 1995;
INIT_LIST_HEAD(&person->list);
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 birthday. We can then add this instance to the end
of the linked list using the list_add_tail() macro:
list_add_tail (&person->list, &birthday_list);
Traversing the Linked List
Traversing the list involves using the list_for_each_entry() Macro, 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 birthday *ptr;
list_for_each_entry(ptr, &birthday_list, list) {
/* on each iteration ptr points */ /* to the next birthday struct */
}
Removing Elements from the Linked List
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 birthday *ptr, *next
list_for_each_entry_safe(ptr, next, &birthday_list, list) {
/* on each iteration ptr points */ /* to the next birthday struct */
list_del(&ptr->list); kfree(ptr);
}
Notice that after deleting each element, we return memory that was previously allocated with kmalloc() back to
the kernel with the call to kfree(). Careful memory management—which includes releasing memory to prevent
memory leaks—is crucial when developing kernel-level code.
Part II Assignment
In the module entry point, create a linked list containing five struct birthday elements. 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. 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.
Experiment information:
Topic:
Linux内核模块
Requirements:
1. 能创建内核模块
2. 能够加载和卸载内核模块
3. 学会检查内核日志缓冲区的内容
4. 学会如何检查当前加载的所有内核模块
5. 掌握内核数据结构链表的构建方法与相应功能的实现
Procedure:
第一部分:创建内核模块
1. ls 查看ch2中内容,找到了目标内核模块
2. make 编译模块,生成多个文件
3. ls -l 详细查看编译后的情况
4. lsmod 加载模块之前先列出当前加载的所有模块(图未截完)
5. sudo insmod simple.ko 内核模块的加载
6. lsmod 查看当前所加载的模块,证明加载成功
7. dmesg 检查消息是否在内核日志缓冲区
8. sudo rmmod simple 删除内核模块
9. lsmod 查看当前模块中是否还有simple
10. dmesg 检查确保模块已被删除
第二部分:内核数据结构
1. 定义链表结构struct
2. 定义插入元素到链表的函数方法
3. 定义删除链表元素的方法
4. 构建遍历链表函数
5. 在模块入口点插入4个元素,遍历输出内容到内核日志缓冲区
6. 在模块退出点从链表中删除元素,并将空闲内存返回内核
Results:
结果图片:
Code:
#include
#include
#include
int day;
int month;
int year;
struct list_head list;
struct birthday *p=NULL;
list_for_each_entry(p,head,list){
}
printk("Month %d Day %d Year %d\n",p->month,p->day,p->year);
struct birthday *n;
n=kmalloc(sizeof(*n),GFP_KERNEL);
n->year=year;
n->month=month;
n->day=day;
INIT_LIST_HEAD(&n->list);
list_add_tail(&n->list,head);
#include
#include
struct birthday{
};
static LIST_HEAD(head);
void list_print(struct list_head *head){
}
void list_insert(struct list_head *head,int year,int month,int day){
}
void list_delete(struct list_head *head){
}
int simple_init(void)
{
struct birthday *n,*p;
list_for_each_entry_safe(p,n,head,list){
}
printk("Loading Modules\n");
list_insert(&head,2017,11,3);
list_insert(&head,2018,10,10);
list_insert(&head,2019,5,1);
list_insert(&head,2020,8,15);
list_print(&head);
return 0;
printk("Removing %d %d %d\n",p->year,p->month,p->day );
list_del(&p->list);
kfree(p);
printk(KERN_INFO "Removing Modules\n");
list_delete(&head);
list_print(&head);
return 0;
}
int simple_exit(void)
{
}
module_init(simple_init);
module_exit(simple_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple Module");
MODULE_AUTHOR("SGG");