Linux 2.6中的模块必须包含以下两个接口:
- module_init(your_init_func);//模块初始化接口
- module_exit(your_exit_func);//模块卸载接口
加载一个内核模块的命令是insmod,格式如下:
- #insmod modulename.ko
卸载一个内核模块的命令是rmmod,格式如下:
- #rmmod modulename
可加载模块的源代码可以放在内核代码树中,也可以独立于内核代码树。如果是后一种情况,就需要为可加载模块编写makefile文件。可加载模块的makefile文件最重要的就是配置如下几个变量:
- CC = arm-linux-gcc
- obj-m := smodule.o
- KERNELDIR ?= /mnt/v/linux-2.6.33.4
CC是编译器,obj-m为需要编译的目标模块,KERNELDIR为内核路径。注意在编写可加载模块前先要有一个内核代码目录树。KERNELDIR的内核版本必须与运行的内核版本一致,否则编译出的模块往往无法加载。
例1.1 最简单的内核模块
代码见光盘\src\1drivermodel\1-1simple。核心代码如下所示:
- static int demo_module_init(void)
- {
- printk("demo_module_init\n");
- return 0;
- }
- static void demo_module_exit(void)
- {
- printk("demo_module_exit\n");
- }
- module_init(demo_module_init);
- module_exit(demo_module_exit);
- MODULE_DESCRIPTION("simple module");
- MODULE_LICENSE("GPL");
模块运行在内核态,不能使用用户态C库函数中的printf函数,而要使用printk函数打印调试信息。编写一个makefile文件如下:
- AR = ar
- ARCH = arm
- CC = arm-linux-gcc
- DEBFLAGS = -O2
- obj-m := smodule.o
- KERNELDIR ?= /mnt/v/urbetter-linux2.6.28-v1.0
- PWD := $(shell pwd)
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules
- clean:
- rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
执行make后生成smodule.ko。运行结果如下:
- [root@urbetter /home]#insmod smodule.ko
- demo_module_init
- [root@urbetter /home]#rmmod smodule
- rmmod: chdir(2.6.28.6): No such file or directory
- [root@urbetter /home]#mkdir -p /lib/modules/`uname -r`
- [root@urbetter /home]#cp smodule.ko /lib/modules/`uname -r`/smodule.ko
- [root@urbetter /home]#rmmod smodule
- demo_module_exit
其中uname -r用来得到内核版本号。如果要使用rmmod命令卸载内核模块,必须在/lib/modules目录下的以内核版本号为名称的目录下建立相应的模块文件。从上面的结果看出,第一次运行rmmod smodule会失败。在建立模块文件后,再次运行rmmod smodule会成功。