linux驱动开发笔记2

来源:互联网 发布:js cookie时间 编辑:程序博客网 时间:2024/05/29 18:48

建立和运行模块

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_initmodule_exit两行代码使用linux宏指明以上两个函数角色。另一个宏MODULE_LICENSE告诉内核模块有一个自由许可证。如果没有这样的声明,内核加载模块时就会抱怨。

2.2编译和加载


内核是一个大的、独立的程序,对于它的各个部分如何组合在一起有详细的明确的要求。在有足够新的编译器、模块工具、其他必要工具和内核源码树前提下,为你的模块创建一个makefile就是直接了当的。对于上面的helloworld例子,单行就够了obj-mhello.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:目标,来运行第2make命令(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);


特别指出在注册时,存在模块加载竞争,如果注册了某特性,可能还没有执行完注册函数,内核其他模块就有可能调用已经注册的模块,请确保要注册的特性内部初始化已经完成再进行注册。另外就是不能完全注册成功时,最好保留部分注册成功的功能,而不要让注册失败。