Linux内核模块
前言
操作系统编程项目Linux内核模块
准备工作
1:准备好Linux环境,下载好make和gcc可参考以下教程
2:Linux下建立Makefile文件
obj-m += simple.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
3:Linux建立内核模块命名为simple.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
/* 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");
4:将simple.c文件和makefile放同一文件中或同一桌面
第一部分:创建内核模块
这个项目的第一部分包括以下一系列步骤,用于创建模块并将其插入到Linux内核。要列出当前加载的所有内核模块,可输入命令
*编译makefile文件*
1.输入make指令得到文件simple.ko
2.编译生成多个文件
- 内模块的加载生成了字符串Loading Module,它是在simple_init()中定义的。说明simple模块已经加载到内核中了。我们可以使用lsmod命令查看,lsmod命令的作用是告诉我们所有在内核中运行的模块的信息,包括模块的名称,占用空间的大小,使用计数以及当前状态和依赖性。
- 安装后移除内模块看到了打印在屏幕上的“Removing Module!”,它是在simple_exit()中定义的。由此说明,simple模块已经被删除。如果这时候我们再使用lsmod查看,会发现simple模块已经不在了。
- 清除缓冲区
第二部分:内核数据结构
在模板入口点,创建链表以包含5个structura birthday元素。遍历链表并且输出内容到内核日志缓冲区。调用dmesg,以确保在模板加载时刻列表构造正确。
在模板退出点,从链表中删除元素,并且将空闲内存返回到内核。另外,调用dmesg,以检查在模块卸载时该列表已经删除。
将文件中多余文件删除(仅留下Makefile和simple.c)
将simple.c的代码改成如下
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/types.h>
struct birthday{
int day;
int month;
int year;
struct list_head list;
};//创建对象
static LIST_HEAD(birthday_list);
/* This function is called when the module is loaded. */
int simple_init(void)
{
static LIST_HEAD(birthday_list);//生成头节点
struct birthday *person; //创建对象
struct birthday *ptr;
//初始化所有变量
person = kmalloc(sizeof(*person), GFP_KERNEL);//分配空间
person->day = 2;//赋值
person->month= 8;
person->year = 1995;
INIT_LIST_HEAD(&person->list);
//连接
list_add_tail (&person->list, &birthday_list);
//遍历
printk(KERN_INFO "Loading Module\n");
list_for_each_entry(ptr, &birthday_list, list)
{
printk(KERN_INFO "Year: %d, Month: %d, Day: %d\n",ptr->year,ptr->month,ptr->day);
}
return 0;
}
void simple_exit(void) {
struct birthday *ptr, *next;
list_for_each_entry_safe(ptr, next, &birthday_list, list)
{
printk(KERN_INFO "Removing Year: %d, Month: %d, Day: %d\n",ptr->year,ptr->month,ptr->day);
list_del(&ptr->list);
kfree(ptr);
}
printk(KERN_INFO "Removing Module\n");
}
module_init( simple_init );
module_exit( simple_exit );
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple Module");
MODULE_AUTHOR("SGG");
在simple_init函数中添加我们将要建立的对象,以及用来遍历的指针person。
给每一个对对象分配空间,并初始化赋值。再使用 list_add_tail(&person1->list, &birthday_list);函数将对象串接起来。最后使用list_for_each_entry(ptr, &birthday_list,list)函数进行遍历。在遍历函数中打印信息。在退出函数中,list_for_each_entry_safe(ptr, next, &birthday_list, list)进行遍历在遍历函数中删除每一个尾部节点,并打印信息,释放分配的空间。
步骤跟第一部分类似
Makefile编译
1.输入make指令得到文件simple.ko
2.遍历链表并输出内容到内容到内核日志缓冲区
3.从链表上删除元素
总结:
1:内核模块的代码编写没有外部的函数库可以用,只能使用内核导出的函数;这点于应用程序是有区别的,应用程序习惯于使用外部的库函数,在编译的时候将程序与库函数链接在一起。比如说:内核模块中不能使用printf(),而只能使用printk()函数。
2:内核模块运行在内核空间,而应用程序在用户空间。应用程序的运行会形成新的进程,而内核模块一般不会。每当应用程序执行系统调用时,linux执行模式从用户空间切换到内核空间。
3.内核是硬件与软件之间的一个中间层。作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。内核就像一个库,提供了一组面向系统的命令。系统调用对于应用程序来说,就像调用普通函数一样。