Linux的内核模块本来就很大,我们添加或者删除某个其中一个模块时,又得重新编译整个模块,导致很多麻烦,那有没有一种机制是将现有的内核并不包含全部功能,当我们需要某个功能时,其对应的代码动态的被加载在内核中
Linux就提供了这种机制叫做模块(module)
简单写一个内核的模块函数:
#include
#include
#include
static int __init hello_init(void)
{
printk("hello world\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("hello world exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LISENCE("GPL v2");
查看内核模块:lsmod /proc/modules—————或者是lsmod /sys/module
加载模块时:可以使用insmod或者modprobe
insmod /path/to/mydvr.ko
modprobe mydvr.ko
modprobe比insmod更高级,加载模块时会同时加载这个模块所依赖的其他模块
卸载模块:使用rmmod或者modprobe -r
若使用modprobe -r 来卸载模块时,会同时卸载模块所依赖的其余模块
而模块的依赖关系被存放在根文件系统 /lib/modules//modules.dep文件中
查看模块信息,所查看的主要是模块的作者、模块的说明、模块所支持的参数等
使用modinfo
一个Linux内核主要由如下几个部分组成:
内核的模块加载函数一般
request_module(module_name);灵活的加载其他内核模块
.init.text
中的代码通常会包含一些初始化函数的调用,而这些初始化函数实际上会被添加到.initcall.init
中,以便在系统初始化过程中被调用。因此,.init.text
中的代码可以被视为初始化代码的入口点,而.initcall.init
包含了实际需要在初始化过程中执行的函数。
static int hello_data __initdata = 1;
许可证(LICENSE)声明描述该内核模块的许可权限
如果不声明LICENSE,模块被加载时将收到内核被污染(Kernel Tainted)的警告
一般的LICENSE可包含:”GPL” “GPL v2” “GPL and additional rights”
使用module_param(参数名,参数类型,参数读/写权限)为模块定义一个参数
static char *name = "my name is xiaoming";
module_param(name, charp, S_IRUGO)
static int data = 6;
module_param(data, int, S_IRUGO);
在装载内核模块时可以通过命令行传递参数值,通过
insmod (或者modprobe)模块名 参数名=参数值
如果不传递,参数的值则会使用模块内定义的缺省值
如果模块被内置,无法用insmod去传参数,则使用bootargs给bootloader里面的模块去传递参数值
参数的类型:
byte、short、ushort、int、uint、long、ulong、charp、bool、inbool
模块参数数组:
module_param_array(数组名,数组类型,数组长度,参数的读写权限);
运行insmod或者modprobe时,应该以逗号分隔输入的数组元素
模块被加载成功时,/sys/module下会存在以模块名命名的目录,当参数(这个参数就是你在模块函数中定义的模块参数)的读写权限为0时,表示此参数不存在sysfs文件系统下对应的文件节点;如果存在参数读写权限不为0的命令行参数,此模块的目录下还将出现parameters目录,目录中包含着一系列以参数名命名的文件节点,这些文件的权限值就是传入module_param()的参数读写权限,而文件的内容为参数的值.
举个例子:
假设有一个名为"example_module"的内核模块,该模块定义了一个名为"my_param"的参数,其读写权限为0644(允许读和写)。加载该模块后,会在/sys/module/example_module目录下出现一个名为"parameters"的子目录,其中包含一个名为"my_param"的文件节点,权限为0644,其内容则为该参数的值。通过使用 tail -n /var/log/messages
就可以查看内核的输出
| 文件或模块参数的权限宏包括:
S_IRUSR:Owner读权限
S_IWUSR:Owner写权限
S_IXUSR:Owner执行权限
S_IRGRP:Group读权限
S_IWGRP:Group写权限
S_IXGRP:Group执行权限
S_IROTH:Universal读权限
S_IWOTH:Universal写权限
S_IXOTH:Universal执行权限
这些权限宏可以组合使用,以表示不同用户对文件或者模块参数的权限设置。 |
---|
在Linux2.4中模块自身通过MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT宏来管理自己被使用的计数
在Linux2.6中模块使用 try_module_get(&module)
和 module_put(&module)
来管理自己被使用的计数
/*该函数用于增加模块使用计数*/
int try_module_get(struct module *module);
/*该函数用于减少模块使用计数*/
void module_put(struct module *module);
struct module *owner//用来指向管理此设备的模块
当某个内核开始使用设备时,内核使用try_module_get去增加管理此设备的owner模块的使用计数;当不再使用的时候,使用函数module_put去减少对管理此设备的管理模块的使用计数;这样当设备在使用的过程中,管理此设备的模块将不会被卸载。只有当设备不再使用时,模块才允许被卸载。
可以先写一个简单的makefile文件
#内核版本号
KVERS = $(shell uname -r)
#内核模块
obj-m += hello.o
#如果hello模块有多个链接文件
#modulename-objs += 1.o 2.o 3.o 这三个文件会被链接进hello.ko中
#定义了总的目标是build,依赖kernel_modules
build: kernel_modules
#定义了目标kernel_modules,内核源码的构建目录为/lib/modules/$(KVERS)/
#M=$(CURDIR)指的是将模块构建在当前的文件夹下
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean