gzg1984@yahoo.com.cn
qq:709581191
1
一个最简单的模块代码文件是什么样子?/* variable.c*/
long module_variable;
虽然可能和你之前所想的不同,但是这一行代码确实可以被编译成模块。
与之相配的最简单的模块Makefile是这样的:
obj-m
:=variable.o
default:
make-C /lib/modules/$(shell uname -r)/build
M=$(shell pwd) modules
2
为毛我没有看见模块的初始化函数或者头文件之类的东西?没有这些东西真的没问题吗?这个模块可以执行吗?因为那些东西不是“模块”的必须组件,当然,一个带有实际功能的模块,一般应该有初始化函数和退出函数。
被标记为module_init的函数会在模块加载时运行。但是模块并不是真的必须包含一个module_init函数。
另外:你始终要记住一点,模块不能“执行”,甚至不能“调用”。加载模块的的过程,不是“执行模块”,也不是“调用模块”,而是将模块代码“加载”到内核代码空间中。
Module_init标记的入口函数,会由insmod进程在完成模块“加载”工作之后进行“调用”。module_init函数的执行过程可以被称为模块“加载”过程中的一个步骤,但并不是唯一的步骤,也不是最重要的步骤,甚至不是必须经过的步骤。
3
如果我什么头文件都不带,那为什么在modinfo中还可以看到vermagic值?那是因为模块的vermagic值不是记录在你写的模块源代码中。
在编译完成上面的模块例子之后,你应该可以看到这个目录下多出了一个variable.mod.c 文件。这个文件会和你写的源代码variable.c编译到一起,构成variable.ko
Vermagic的值,是通过variable.mod.c文件引入variable.ko的。
4
那么这个最简单的模块有什么用吗?一点用也没有。
内核中的其他代码也无法访问到这个变量。
5
一个有一点用处的最简单的模块应该是什么样子?/* variable.c*/
static long module_variable;
EXPORT_SYMBOL(module_variable);
Makefile不变。
这样一个模块,应该无法被内核已经有的模块使用,但是却可以为你以后写的所有模块提供一个公用的变量。当然,如果你真打算把它当做模块间同步的工具,你还应该为它设计一个锁。
6
导出和static修饰不冲突吗?导出和”static”概念有区别:
1:static在内核中的含义也只是限定一个变量只有本文件可以使用。
2:Static限制的变量也可以导出。
3:所有导出了的变量名都是唯一的。
4:只要导出了的变量就可以在其他模块使用,无论次变量是否static修饰。
5:一个变量或函数能不能由本模块中的其他文件使用,是由static修饰决定,规则与普通C语言规则一样,无论此函数是否导出。
总结地说,static的限制范围作用于模块本身以及模块自己的编译过程。导出的作用范围是模块加载入内核后的内核符号表。
7
一个带有函数的最简单的模块是什么样子?#include <linux/module.h>
static int test1(void)
{
printk("it's test1 .\n");
return 0;
}
EXPORT_SYMBOL(test1);
这不再是一个完全没用的例子了。你可以写一个新的模块来调用test1函数。
8
已经导出的函数怎么使用?可以直接调用。当然,需要先声明原型。
但是注意,如果你的模块里面也有一个test1,那么你会调用到你自己模块里的test1而不是其他模块导出的test1.
9
我怎么知道谁导出了什么函数?/proc/kallsyms文件中有内核代码空间中所有的函数,格式是这样的:
f7c42254
t
find_nls
[nls_base]
f7c42073
T
utf32_to_utf8
[nls_base]
中间的t代表没有导出,T代表导出的函数。
10
模块参数的用法int value = 5;
char *s = "hello dog";
module_param(value, int, S_IWUSR |S_IRUGO);
module_param(s, charp, S_IWUSR | S_IRUGO);
以上就声明了两个模块函数,可以在加载模块时使用value=X之类的方法设置这些置。不设置的话,这些变量保存的就是默认值。Module_param的第三个参数是指的这个参数对应的/sys目录下的文件的访问权限。所有模块参数都会各自被抽象成一个文件,存放在/sys/modules目录下面。这里的module_param的第三个参数,就是约束那个文件的访问权限的。
11
模块参数数组的用法static unsigned int many[5] = {1, 2, 3, 4,5};
static int num = 20;
module_param_array(many, uint, &num,0444);
将以上代码加入到模块代码中,就可以使用模块参数数组了。
模块参数数组的大小,模块代码会自己去计算,和第三参数num没有关系。
Num的作用是记录传入参数的个数。
传入参数的办法:
insmod param_array.ko many=1,3,5,7
这样可以将many数组的前4个值修改为1,3,5,7,但是第5个值没有变化。
Num的值会变成4
12
内核污染问题当新加载的模块未声明许可证或者许可证不被内核认可的时候,内核将输出kernel tainted警告。这种情况,一般被称为内核污染。
声明许可证的方法:
MODULE_LICENSE(“licensename”)
声明许可证的举例
MODULE_LICENSE("Dual BSD/GPL");
MODULE_LICENSE("GPL");
MODULE_LICENSE("BSD ");
13
符号导出问题导出方法有以下两种
EXPORT_SYMBOL
EXPORT_SYMBOL_GPL
导出的“符号”可以是变量,也可以是函数,但是主要功能是导出函数。
在最新版的内核中使用这种办法导出变量,在编译过程中会产生警告
14
模块版本问题CONFIG_MODVERSIONS内核选项打开后,加载模块时,内核将会强制验证模块的版本,如果和内核版本不一致,内核会拒绝加载。
模块的内核版本记录在于模块一起编译的.mod.c文件中,一般形式如下:
MODULE_INFO(vermagic, VERMAGIC_STRING);
v2.6.34.8的menuconfig中只有version检查的选项,
但是(不知道是内核升级还是发布版定制导致),后续还出现了校验内核接口CRC的版本校验,导致即使同一个小版本号的内核,只要源码树不同,编译出的模块就可能无法互通。
解决办法:
1:模块与内核在同一个目录树下编译
2:解除内核的版本检查机制。
15
自动drop的代码段当一个函数被标注为__init的时候,它将被加载并使用一次,然后保存其代码的内存将被释放。这是一个半自动的过程:编程者指定哪些代码只用一次,而内核按照编程者的指定丢弃使用过的代码。
一般模块加载时的入口函数会被标记为__init。这个函数与模块中其他函数的不同就在于:其他函数将被加载进内核的代码空间中,直到卸载模块为止;而这个“初始化函数”,开始时也会被加载进内核的代码空间中,但是内核在调用过一次后,就会将这段代码自动自动丢弃掉,内核的代码空间中再也看不见这个函数了。