这一节主要内容讲解linux内核的模块机制。主要参考经典书籍《linux device drivers》。
① 大多数小规模及中规模的应用程序从头到尾执行单个任务,而模块却只是预先注册自己一边服务于将来的某个请求,然后他的初始化函数就立即结束了。
模块仅仅被链接到内核,因此它能调用的函数仅仅是由内核导出的那些函数,而不存在任何可链接的函数库。所以源文件不能包含常用的头文件。内核模块只能使用作为内核一部分的函数。大多数相关的头文件保存在内核源码树的(/home/sml/linux2.6.37.4/)头文件声明里,include/linux和include/asm目录中。
② 最简单的模块构造(helloworld)
1. 在虚拟机上面创建hello.c和Makefile。
2. Hello.c源代码如下:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
static int hello_init(void)
{
printk(KERN_EMERG "hello world!\n");
return 0 ;
}
static void hello_exit(void)
{
printk(KERN_EMERG "crule world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
3. makefile文件内容
obj-m := hello.o
4. 虚拟机上必须要有内核源码树,我的在/home/sml/linux2.6.37.4,因为我们构造的模块最终要运行的linux2.6.37.4内核上面,所以要在内核源码树下面编译模块。执行make -C/home/sml/linux2.6.37.4 M=$PWD modules.
root@sml-VirtualBox:/home/sml/nfs_server#make -C /home/sml/linux-2.6.37.4 M=$PWD modules
make:进入目录'/home/sml/linux-2.6.37.4'
CC[M] /home/sml/nfs_server/hello.o
Buildingmodules, stage 2.
MODPOST 1modules
CC /home/sml/nfs_server/hello.mod.o
LD[M] /home/sml/nfs_server/hello.ko
make:离开目录“/home/sml/linux-2.6.37.4”
执行完命令,生成hello.ko模块。
5. make-C /home/sml/linux2.6.37.4 M=$PWD modules解析。
前面讲到,模块依赖于内核导出的一些符号,主要的头文件声明在include/linux和include/asm目录下。所以编译模块肯定要依赖于内核源码树。-C 表示make切换到指定目录。M是makefile中的一个变量,指定为当前目录,也就是hello.c 的所在目录。Modules指向obj-m变量中设定的模块,就是hello模块。因为在hello.c的Makefile中没有指定内核源码树的目录,所以-C来告诉make内核源码树的路径。(这个地方理解的不是很透彻,但是我们要明白:内核模块的编译是依赖于内核源码树的。)。
6. 模块的装载和卸载
装载命令:insmod;卸载命令:rmmod;查看模块命令:lsmod;查看模块信息:modinfo;
我们把上面的编译的模块放在根文件系统上面,然后执行装载命令,即可看到我们要打印的信息。如何放在开发板的根文件系统上,请参见:NFS实现开发板和虚拟机之间的文件共享。Make工具的使用方法请参见:MAKE工具介绍。
③ 内核符号表(模块层叠技术)
Insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(变量和函数)的地址,这是实现模块化驱动程序的必须的。当模块被装入内核后,它所导出的任何符号都会成为内核符号表的一部分。新模块可以使用有我们自己的模块导出的符号。这样我们可以在其他模块上层叠新的模块(模块层叠技术)。符号导出实现模块之间的资源共享。驱动程序的模块层叠可以理解为抽象层为硬件驱动层提供符号,抽象层和硬件驱动层就相当于两个(或多个)模块。我们可以将模块划分为多个层,缩短开发时间。
符号导出方法:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
④ 初始化和关闭
module_init(hello_init);
module_exit(hello_exit);
初始化和关闭是强制性的,是模块被insmod和rmmod时执行的。
⑤ 模块的参数
内核允许驱动程序指定参数,而这些参数可以在装载驱动程序模块时改变。驱动程序参数包括设备编号,以及一些其他的控制驱动程序操作方式的参数。如:
insmod hellop howmany=10 whom=”sml”
在模块中,参数的申请方法,包含头文件
#include <moduleparam.h>
格式为:
Module_param(name,type,s perm);
如:
Module_param(howmany,int,S_IRUGO);
S_IRUGO表示任何人可读取参数,但是不能更改;
S_IRUGO|S_IWUSR表示允许root用户修改该参数。大多数情况下,我们不应该让模块参数可写。