建立和运行模块
2.1模块基本构成
最简单的helloworld模块示例代码如下:
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("DualBSD/GPL");
staticint hello_init(void)
{
printk(KERN_ALERT"Hello, world\n");
return0;
}
staticvoid hello_exit(void)
{
printk(KERN_ALERT"Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
该模块定义了两个函数,hello_init在模块被加载时调用,hello_exit在模块被删除时调用。module_init和module_exit两行代码使用linux宏指明以上两个函数角色。另一个宏MODULE_LICENSE告诉内核模块有一个自由许可证。如果没有这样的声明,内核加载模块时就会抱怨。
2.2编译和加载
内核是一个大的、独立的程序,对于它的各个部分如何组合在一起有详细的明确的要求。在有足够新的编译器、模块工具、其他必要工具和内核源码树前提下,为你的模块创建一个makefile就是直接了当的。对于上面的helloworld例子,单行就够了obj-m:hello.o.这种写法利用了GNU-make提供的扩展语法,编译出的模块名为hello.ko。反之,如果如果有个模块名为module.ko,来自2个原文件,正确书写:
obj-m:=module.o
moduleobjs:= file1.o file2.o
假设上面的内核源码位于~/kernel-2.6,用来建立你的模块的make命令(模块源码目录)
make-C ~/kernel-2.6 M=‘pwd’ modules
这个命令开始是改变它的目录到用-C选项提供的目录下(就是说,你的内核源码目录).它在那里会发现内核的顶层makefile.这个 M=选项使 makefile在试图建立模块目标前,回到你的模块源码目录.这个目标,依次地,是指在 obj-m变量中发现的模块列表,在我们的例子里设成了module.o.
上面的方法仍然麻烦,可以采用下面更简单的方法,这充分类用了makefile的扩展特性。
#If KERNELRELEASE is defined, we've been invoked from the
#kernel build system and can use its language.
ifneq($(KERNELRELEASE),)
obj-m:= hello.o
#Otherwise we were called directly from the command
#line; invoke the kernel build system.
else
KERNELDIR?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE)-C $(KERNELDIR) M=$(PWD) modules
endif
再一次,我们看到了扩展的GNUmake 语法在起作用.这个 makefile在一次典型的建立中要被读2次.当从命令行中调用这个makefile, 它注意到KERNELRELEASE变量没有设置.它利用这样一个事实来定位内核源码目录,即已安装模块目录中的符号连接指回内核建立树.如果你实际上没有运行你在为其而建立的内核,你可以在命令行提供一个KERNELDIR=选项,设置KERNELDIR环境变量,或者重写makefile中设置 KERNELDIR的那一行.一旦发现内核源码树,makefile 调用default:目标,来运行第 2个 make命令(在 makefile里参数化成$(MAKE))象前面描述过的一样来调用内核建立系统.在第 2次读,makefile 设置obj-m,并且内核的makefile文件完成实际的建立模块工作。
模块编译完成,一般使用insmod加载(rmsmod卸载),这个程序加载模块的代码段和数据段到内核,然后执行符号链接。另一个工具modprobe,如同insmod,他能够检测依赖关系,将依赖模块装载,比insmod强大。
2.3初始化和关停
staticint __init initialization_function(void)
{
/*Initialization code here */
}
module_init(initialization_function);
staticvoid __exit cleanup_function(void)
{
/*Cleanup code here */
}
module_exit(cleanup_function);
特别指出在注册时,存在模块加载竞争,如果注册了某特性,可能还没有执行完注册函数,内核其他模块就有可能调用已经注册的模块,请确保要注册的特性内部初始化已经完成再进行注册。另外就是不能完全注册成功时,最好保留部分注册成功的功能,而不要让注册失败。