一、模块参数
根据不同应用场合向 内核模块
传递不同参数,提高内核模块灵活性。
定义一个常见变量可以通过使用 module_param 宏
把传参值赋给变量。
然后在shell下: insmod xxx.ko key1=val1 key2=val2 ...
可以将参数传递给此内核模块。
module_param(name, type, perm)
name:参数名
type:参数类型,如int、byte、bool、...
注意 char 在此处应该使用 byte,char* 在此处应该使用 charp。
perm:读写权限,注意不允许设置可执行权限,八进制数串,以0开头,如0777。
模块加载以后,可以在 /sys/module/模块名/parameters
目录下,会生成该参数对应的同名文件(对应变量需指明 r 权限),cat其可以打印变量值,该文件的内容与对应变量的实时值同步。
模块卸载以后,这些变量同名文件消失。
注意函数第三个参数 perm
若rwx中的r权限不开,你会发现这些变量同名文件不存在。
开了w权限可以在shell下修改其值。另外开了 w 权限必须也要开 r 权限,否则编译报错。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
/*
* itype 未指定读权限,在 /sys/module/test/parameter
* 目录下找不到该变量名,下面三个变量都能在该目录下体现
*/
static int itype=0;
module_param(itype,int,0);
static bool btype=0;
module_param(btype,bool,0644);
/* 注意字符类型对应下面第二个参数:byte */
static char ctype=0;
module_param(ctype,byte,0644);
/* 注意字符指针类型对应下面第二个参数:charp */
static char *stype=0;
module_param(stype,charp,0644);
static int __init param_init(void) {
printk(KERN_ALERT "param init!\n");
printk(KERN_ALERT "itype=%d\n",itype);
printk(KERN_ALERT "btype=%d\n",btype);
printk(KERN_ALERT "ctype=%d\n",ctype);
printk(KERN_ALERT "stype=%s\n",stype);
return 0;
}
static void __exit param_exit(void) {
printk(KERN_ALERT "module exit!\n");
printk(KERN_ALERT "itype=%d\n",itype);
printk(KERN_ALERT "btype=%d\n",btype);
printk(KERN_ALERT "ctype=%d\n",ctype);
printk(KERN_ALERT "stype=%s\n",stype);
}
module_init(param_init);
module_exit(param_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author");
MODULE_DESCRIPTION("module_param");
MODULE_ALIAS("module_param");
- 测试
只有指定了读权限
,才能在/sys/module/模块名/parameters
目录下看到对应的模块参数名。
只有指定了写权限
,才改变这个模块参数变量的值。
不指定读权限
指定写权限
,编译报错。
echo1 个字串
到1个文件
,会自动添加1个换行符
在null-terminated
之前。
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # modprobe test.ko itype=66 btype=1 ctype='6' stype="666"
param init!
itype=66
btype=1
ctype=6
stype=666
/lib/modules/4.1.15 #
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # ls /sys/module/test/parameters/
btype ctype stype
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # cat /sys/module/test/parameters/btype
Y
/lib/modules/4.1.15 # cat /sys/module/test/parameters/ctype
6
/lib/modules/4.1.15 # cat /sys/module/test/parameters/stype
666
/lib/modules/4.1.15 # echo "888" > /sys/module/test/parameters/stype
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # cat /sys/module/test/parameters/stype
888
/lib/modules/4.1.15 # rmmod test
module exit!
itype=66
btype=1
ctype=6
stype=888
/lib/modules/4.1.15 #
二、符号共享
内核模块可以共享 导出的符号表
,然后在不同的内核模块之间共享。
可以导出 / 共享 变量
或者 函数
。
通过此宏进行变量或者函数的导出。
EXPORT_SYMBOL(sym);
// sym:变量名或者函数名
// 注意使用别的模块的变量函数的模块也要在其c文件中包含声明的头文件,否则gcc编译都不过了
1、查看符号表
cat /proc/kallsyms | grep xxx
xxx 是想要查询的符号名称。
2、模块手动加载
insmod
rmmod
加载时,必须先加载相关依赖模块。
卸载时,顺序相反。
3、模块自动加载
所有内核模块需要统一放到 /lib/modules/内核版本
目录下:cp *.ko /lib/modules/内核版本
。
自动建立模块依赖关系:depmod -a
,建立完毕可以查看模块依赖关系: cat /lib/modules/内核版本/modules.dep
。
加载模块及其依赖模块:modprobe xxx
(注意用 modprobe 加载模块时可以自动预加载此模块依赖的模块)。
卸载模块及其依赖模块(.ko
后缀名可以省略):modprobe -r xxx
(会自动卸载)。
4、模块加载卸载总结
insmod xxx.ko
:加载内核模块,若此模块依赖别的内核模块,需要事先手动加载好(.ko
后缀名不可以省略,必须要在模块所在目录)。
modprobe xxx.ko
: 加载内核模块,若此模块依赖别的内核模块,会自动去加载(.ko
后缀名可以省略,不必要在模块所在目录)。
rmmod xxx.ko
:卸载内核模块,只卸载当前模块(.ko
后缀名可以省略,不必要在模块所在目录)。
modprobe -r xxx.ko
:卸载内核模块,该模块依赖的其它模块也会被卸载(.ko
后缀名可以省略,不必要在模块所在目录)。
modprobe
会自动分析模块的依赖关系,cat /lib/modules/内核版本/modules.dep
可以获取当前模块的依赖关系。
rmmod xxx.ko
卸载某个模块时,若这个模块被另一个模块依赖,那么会报错。
- module_param.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
static int itype=0;
module_param(itype,int,0);
static bool btype=0;
module_param(btype,bool,0644);
/* 注意字符类型对应下面第二个参数:byte */
static char ctype=0;
module_param(ctype,byte,0);
/* 注意字符指针类型对应下面第二个参数:charp */
static char *stype=0;
module_param(stype,charp,0644);
static int __init param_init(void) {
printk(KERN_ALERT "param init!\n");
printk(KERN_ALERT "itype=%d\n",itype);
printk(KERN_ALERT "btype=%d\n",btype);
printk(KERN_ALERT "ctype=%d\n",ctype);
printk(KERN_ALERT "stype=%s\n",stype);
return 0;
}
static void __exit param_exit(void) {
printk(KERN_ALERT "module exit!\n");
printk(KERN_ALERT "itype=%d\n",itype);
printk(KERN_ALERT "btype=%d\n",btype);
printk(KERN_ALERT "ctype=%d\n",ctype);
printk(KERN_ALERT "stype=%s\n",stype);
}
/* 导出变量 */
EXPORT_SYMBOL(itype);
int my_add(int a, int b) {
return a+b;
}
/* 导出函数 */
EXPORT_SYMBOL(my_add);
int my_sub(int a, int b) {
return a-b;
}
/* 导出函数 */
EXPORT_SYMBOL(my_sub);
module_init(param_init);
module_exit(param_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author");
MODULE_DESCRIPTION("module_param");
MODULE_ALIAS("module_param");
- calculation.h
#ifndef __CALCULATION_H__
#define __CALCULATION_H__
extern int itype;
int my_add(int a, int b);
int my_sub(int a, int b);
#endif
- calculation.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
/* 这个头文件声明了导出的变量和函数的声明。 */
#include "calculation.h"
static int __init calculation_init(void)
{
printk(KERN_ALERT "calculation init!\n");
printk(KERN_ALERT "itype+1 = %d, itype-1 = %d\n", my_add(itype,1), my_sub(itype,1));
return 0;
}
static void __exit calculation_exit(void)
{
printk(KERN_ALERT "calculation exit!\n");
}
module_init(calculation_init);
module_exit(calculation_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author");
MODULE_DESCRIPTION("calculation module");
MODULE_ALIAS("calculation_module");
- 测试
只有不被依赖的模块才能被卸载(rmmod
或modprobe -r
),modprobe -r
会把要卸载模块及其依赖的模块一并卸载掉。。
上面的两个 c文件可以分来编译,只要加载的顺序对了就可以。
卸载的顺序和加载的顺序相反。
/lib/modules/4.1.15 # modprobe test.ko itype=66 btype=1 ctype='6' stype="666"
param init!
itype=66
btype=1
ctype=6
stype=666
/lib/modules/4.1.15 #
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # modprobe a.ko
calculation init!
itype+1 = 67, itype-1 = 65
/lib/modules/4.1.15 # rmmod test.ko
rmmod: can't unload module 'test': Resource temporarily unavailable
/lib/modules/4.1.15 #
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # modprobe -r test
modprobe: can't unload module 'test': Resource temporarily unavailable
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # lsmod
Module Size Used by Tainted: G
a 713 0
test 1815 1 a
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # rmmod a
calculation exit!
/lib/modules/4.1.15 # rmmod test
module exit!
/lib/modules/4.1.15 #