LINUX内核模块的意义:
允许内核在运行时动态地向其中插入或从中删除代码.
16.1 构建模块
我们经常做的事情就是往内核源码树添加我们自己的驱动源码,便交付内核去编译.下面以一个例子来说明此流程.
比如说,我们有个驱动,涉及到两个源码文件:fishing-main.c和fishing-line.c.而且要独立于一个文件夹下.假设我们的驱动是字符设备.那么,我们可以在/deivers/char/目录下建立一个名为fishing的子目录.因此,我们的源码位于/deivers/char/fishing/目录下.我们必须告诉内核到我们指定的目录下去编译我们的源码,通过以下方式告诉内核进入指定的目录:
方式一:必须编译
修改drivers/char/Makefile文件中加入:
Obj-m +=fishing/
这行代码的意思就是告诉模块构建系统编译模块时需要进入fishing/子目录中.
方式二:选择性编译
我们的驱动模块是否参与编译可以通过特殊配置选项来裁决.这也是我们常用的手段.比如可能的CONFIG_FISHING_POLE(通过修改drivers/char/Kconfig文件).如下:
Obj-$(CONFIG_FISHING_POLE) += fishing/
好了,内核进入了我们指定的目标目录,下面就要求内核把我们的目标源码文件参与编译.当然,首先需要添加一个新的Makefile文件.编译方式也有两种:
方式一:必须编译
Obj-m += fishing.o
方式二:选择性编译
Obj-$(CONFIG_FISHING_POLE) += fishing.o
当然,出现两个或以上的文件时,Makefile需要作如下修改:
Obj-$(CONFIG_FISHING_POLE) += fishing.o
Fishing-objs := fishing-main.o fishing-line.o
这样fishing-main.c和fishing-line.c就一起编译和连接到fishing.ko模块内.
最后,如果在构建文件时需要额外的编译标记,只需要在Makefile中添加如下指令:
EXTRA_CFLAGS += -DTITANIUM_POLE
16.1.2 放在内核代码外
在目标源码树目录下建立一个Makefile文件,内容如下:
Obj-m := fishing.o
这样就可以把fishing.c编译成fishing.ko.
如果存在多个源文件,Makefile内容如下:
Obj-m := fishing.o
Fishing-objs := fishing-main.o fishing-line.o
这样一来,fishing-main.c和fishing-line.c就一起被编译和连接fishing.ko模块内.
16.2 安装模块
当我们编译好内核,需要编译我们自己的模块源码时.执行如下源码:
Make modules_install
16.3 产生模块依赖性
LINUX模块之间存在依赖性.内核依赖关系的信息,可以通过下面的命令查看:
Depmod
如果只为新模块生成依赖信息,而不是生成所有的依赖关系,通过下面命令查看:
Depmod –A
模块的依赖关系信息存放在/lib/modules/version/modules.dep文件中
16.4 载入模块
最简单的命令:insmod
加载驱动:
Insmod module
卸装驱动:
Rmmod module
比较先进的工具:modprobe.它提供了模块依赖性分析,错误的智能检查报告及其他功能和选项.如果当前装载有依赖其他模块,将自动装载其所依赖的模块.强烈建议使用此工具:
装载:
Modprobe module [module parameters]
Module指定了需要载入的模块名称,后面的参数将在模块加载时传入内核.
卸载:
Modprobe –r modules
卸载过程中会自动卸载其依赖的模块.
16.5 管理配置选项
导入自定义Kconfig文件:
本节主要围绕Kconfig文件展开.Kconfig文件是内核组织编译的一种手段,其中对Makefile有效的内容意义相当于变量--不同的变量值(有三个,m,y,n)去控制Makefile文件的编译动作,是编译进内核还是模块还是索性就不编译.比如我们在drivers/char/fishing目录下建立了一个Kconfig文件,需要在企图上一级Kconfig引入此Kconfig文件.例如我们可以在drivers/char目录下的Kconfig文件添加下面一行指令:
Source "drivers/char/fishing/Kconfig"
在Kconfig文件中加入一个配置选型:
Config FISHING_POLE
Tristate "Fish Master XL support"
Default n
Help
If you say Y here,support for the Fish Master XL 2000....
If unsure,say N.
驱动的帮助信息
配置选项第一行定义了该选项代表的配置目标.注意CONFIG_前缀产需要写上.
第二行声明选项类型为tristate,也就是说可以被编译进内核(Y),也可作为模块编译(M).或者不编译它(N).
第三行是选项的默认选择,这里默认操作是不编译它.
Kconfig文件的意思请参考其他相关资料.
16.6 模块参数
模块参数对于驱动程序而言属于全局变量,也将出现在sysfs文件系统中.定义一个模块参数通过module_param()完成.
Module_param(name,type,perm);
各参数的意义如下:
Name:参数名;
Type:name的类型,如char,int等;
Perm:在sysfs文件系统下对应文件的权限,该值是八进制的格式,比如0644(所有者可读写,组内可读,其他人可读).此值为0表示禁止所有sysfs项.
使用模块变量的示意代码如下:
Static int allow_live_bait = 1;
Module_param(allow_live_bait,bool,0644);
如果模块的外部参数名称不同于它对应的内部变量名称,这时就该用宏module_param_name()定义.示意代码如下:
Module_param_named(name,variable,type,perm);
Name是外部可见参数名称,参数variable是对数对应的内部全局变量名称.如:
Static unsigned int max_test = DEFAULT_MAX_LINE_TEST;
Module_param_named(maximum_line_test,max_test,int,0);
更常见的是,需要一个charp类型定义模块参数(一个字符串),内核将用户提供的这个字符串拷贝到内存,将你的变量指向该字符串.如:
Static char *name;
Module_param(name,charp,0);
更多的模块参数见LDD3.
16.7 导出符号表
主要完成模块间通讯的一种接口.通过EXPORT_SYMBOL()指令导出.实际使用的示意代码如下:
Int get_pirate_beard_color(void)
{
Return pirate->beard->color;
}
EXPORT_SYMBOL(get_pirate_beard_color);
这样,get_pirate_beard_color函数也可以被其他模块调用.