linux内核的框架很大,组件很多,如果把所有东西全编译进内核,内核会很大,如果我们要进行修改时,还要重新编译内核,耗时费力.linux的模块机制解决了这样的问题,模块本身不编译进内核,控制了内核的大小,模块一旦被加载,和内核中其他部分一样.
一.简单的例子
说的多,不如贴代码,一个简单的例子程序如下:
/*bike.c*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static char * bike_name = "mobike";
module_param(bike_name,charp,S_IRUGO);
static int bike_num = 3000;
module_param(bike_num,int,S_IRUGO);
static int __init bike_init(void)
{
printk(KERN_INFO "hello bike module\n");
printk(KERN_INFO "bike-name:%s\n",bike_name);
printk(KERN_INFO "bike_num:%d\n",bike_num);
return 0;
}
module_init(bike_init);
static void __exit bike_exit(void)
{
printk(KERN_INFO "bike module exit\n");
}
module_exit(bike_exit);
MODULE_AUTHOR("Trice");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple program for testing kernel module");
MODULE_VERSION("V1.0");
一个内核模块主要由:
1.模块加载函数
2.模块卸载函数
3.模块声明
4.模块参数(可选)
5.模块作者(可选)
模块加载函数
static int __init bike_init(void)
{
printk(KERN_INFO "%s\n",bike_name);
printk(KERN_INFO "%d\n",bike_num);
return 0;
}
module_init(bike_init);
linux中,标识为__init的函数如果直接编译进内核,连接时会放在.init.text段,初始化时,内核会调用这些__init函数,完成后释放.类似数据可以标识为__initdata.
所以bike_init以__init标识,加载时用module_init指定.
模块卸载函数
static void __exit bike_exit(void)
{
printk(KERN_INFO "bike module exit\n");
}
module_exit(bike_exit);
卸载函数用__exit标识,用module_exit指定
模块声明
MODULE_AUTHOR("Trice");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple program for testing kernel module");
MODULE_VERSION("V1.0");
声明一些信息,其中MODULE_LICENSE("GPL v2");
声明模块的许可权限.
模块参数
static char * bike_name = "mobike";
module_param(bike_name,charp,S_IRUGO);
static int bike_num = 3000;
module_param(bike_num,int,S_IRUGO);
使用module_param(参数名,类型,参数读写权限)
,charp表示字符指针,传递时采用insmod 模块名 参数名=参数值
,S_IRUGO,表示读,U->user G->group O->other.
二.模块的编译
上面的bike.c可以用下面的Makefile进行编译
KVERS = $(shell uname -r)
#kernel module
obj-m += bike.o
build:kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
obj-m += bike.o
这种在内核编译中很常见,意思把bike.c编成模块,进入相应目录使用里面Makefile进行编译,M=$(CURDIR)
指定目标.
如果有多个文件可以使用:
obj-m := xx.o
xx-objs := file1.o file2.o
三.测试结果
huang@ubuntu:~/test$ ls
bike.c Makefile
huang@ubuntu:~/test$
huang@ubuntu:~/test$
huang@ubuntu:~/test$ make
make -C /lib/modules/3.16.0-30-generic/build M=/home/huang/test modules
make[1]: Entering directory `/usr/src/linux-headers-3.16.0-30-generic'
CC [M] /home/huang/test/bike.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/huang/test/bike.mod.o
LD [M] /home/huang/test/bike.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.16.0-30-generic'
huang@ubuntu:~/test$
huang@ubuntu:~/test$
huang@ubuntu:~/test$ ls
bike.c bike.mod.c bike.o modules.order
bike.ko bike.mod.o Makefile Module.symvers
huang@ubuntu:~/test$
huang@ubuntu:~/test$ sudo insmod bike.ko
huang@ubuntu:~/test$ lsmod | grep bike
bike 12665 0
huang@ubuntu:~/test$
huang@ubuntu:~/test$ dmesg | tail -3
[19695.556447] hello bike module
[19695.556453] bike-name:mobike
[19695.556456] bike_num:3000
huang@ubuntu:~/test$
huang@ubuntu:~/test$
huang@ubuntu:~/test$ sudo rmmod bike
huang@ubuntu:~/test$
huang@ubuntu:~/test$ lsmod | grep bike
huang@ubuntu:~/test$
huang@ubuntu:~/test$ dmesg | tail -1
[19804.797345] bike module exit
huang@ubuntu:~/test$ sudo insmod bike.ko bike_name=ofo bike_num=666
huang@ubuntu:~/test$ dmesg | tail -3
[20692.083907] hello bike module
[20692.083913] bike-name:ofo
[20692.083915] bike_num:666
huang@ubuntu:~/test$ sudo rmmod bike;dmesg | tail -1
[20760.653996] bike module exit
insmod rmmod lsmod
分别用于装载 卸载 查看模块
dmesg
用于显示打印信息
tail -n
显示最后n行
四.总结
内核模块只是把.c文件编译成.ko文件,其实就是一种目标文件的形式,然后在装载时,由内核实现模块的链接运行,同时,在内核加载和卸载的过程中,会通过函数回调用户定义的模块入口函数和模块出口函数,实现相应的功能。