1.目标:
学习Linux中内核模块的编写,实现该模块的装载与卸载。
在Linux中,内核模块的保存在 /lib/modules/ 内核版本 /kernel/ 目录中。
2.编写模块源代码:
# include <linux/module.h> //任何模块都必须包含,定义了可动态加载到内核的模块所需要的必要信息
# include <linux/init.h> //必须包含,包含了宏__init(指定初始化函数)和__exit(指定清除函数)
# include <linux/kernel.h> //里面包含常用的内核API,例如内核打印函数printk()
static int __init zyp_init(void) //__init将函数zyp_init()标记为初始化函数,在模块被装载到内核时调用zyp_init()
{
int sum = 0;
int i;
for(i = 1; i < 11; i++) //函数功能为1-10累加求和
sum +=i;
printk(KERN_INFO "Hello kernel\n"); //注意末尾不要忘记加换行\n,否则打印会出现某些小的错误
printk(KERN_INFO "sum is %d\n",sum);
//打印级别设为KERN_INFO,将求和结果立即打印,可以在插入模块后,在用户态下用命令dmesg查看打印效果
return 0;
}
static void __exit zyp_exit(void) //清除函数,在模块被卸载之前调用
{
printk(KERN_INFO "Goodbye kernel\n"); //在模块卸载时,将Goodbye kernel这句话打印到日志
}
module_init(zyp_init); //引导内核 模块从这里进来
module_exit(zyp_exit); //引导内核 模块从这里出去
MODULE_LICENSE("GPL"); //(必选项) 模块许可证,缺省此句,将导致内核被污染
MODULE_AUTHOR("zyp"); //(可选项) 描述模块作者
MODULE_DESCRIPTION("ADD"); //(可选项) 描述模块功能
注:
(1)__ exit标记该段代码仅用于模块卸载,被标记为 __ exit的函数只能在模块被卸载或者系统关闭时调用。如果一个模块未被定义为清除函数,则内核不允许卸载该模块。
3.准备MakeFile文件
obj-m+=zyp.o #make会自动将源程序zyp.c编译为目标程序zyp.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules#编译模块
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules #清理
注:Makefile要求使用制表符进行缩进,如果使用了空格代替了制表符(TAB)来进行缩进会报错,如下:
Makefile:4: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.
4.对上述文件进行编译
make
如图所示编译成功:
我们的目录文件多出了.ko文件,表示成功:
5.运行
跟普通程序直接运行不同,内核模块需要加载到内核里面发挥作用,可使用 insmod 命令加载内核模块,并使用 lsmod 命令查看已经装载的内核模块:
因为该模块装载到内核时会调用 zyp_init(void) 函数,所以我们通过 dmesg 命令查看执行结果,部分截图:
6.卸载模块
通过 rmmod 命令卸载模块,并使用lsmod进行查看:
可以看到 zyp 模块已经不存在了。