文章目录
背景
有些时候内核模块需要动态设置参数。本文简单介绍如何在ko中添加以及如何设置,以及如何查看ko有哪些参数的方法。
要点
- 主体分几个定义参数,指定module_param参数,以及指定描述。
- module_param本质是定义一个struct kernel_param的结构,并且有标准的回调处理函数的
- 也可以使用module_param_cb来直接定义一个自定义的处理函数,类似doca的argp模块。是修改_param代码段的信息,包括有struct kernel_param_ops
- MODULE_PARM_DESC本质是去修改.modinfo代码段的信息
- 定义设置:
static int param1 = 0;
// 模块参数
module_param(param1, int, S_IRUGO); //注意这里的参数
MODULE_PARM_DESC(param1, "Debug level (0-2)");
- insmod指定:insmod x.ko xxx=xxx
insmod param.ko param1=100
- 参数可以指定:
S_IRUSR(Read by owner):文件所有者有读权限。
S_IWUSR(Write by owner):文件所有者有写权限。
S_IRGRP(Read by group):文件所在组有读权限。
S_IWGRP(Write by group):文件所在组有写权限。
代码
ko文件param.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
// 全局调试级别变量
static int param1 = 0;
// 模块参数
module_param(param1, int, S_IRUGO);
MODULE_PARM_DESC(param1, "Debug level (0-2)");
// 初始化函数
static int __init param1_init(void) {
printk(KERN_INFO "param1: Initializing module with param1: %d\n", param1);
return 0;
}
// 清理函数
static void __exit param1_exit(void) {
printk(KERN_INFO "param1: Exiting module\n");
}
// 模块宏定义
module_init(param1_init);
module_exit(param1_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("beiming");
MODULE_DESCRIPTION("A simple module with a parameter");
MODULE_VERSION("0.01");
Makefile
obj-m += param.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
实操
-
编译: make
-
安装与日志:insmod param.ko
其他
查看某个ko有哪些参数modinfo
内核实现原理初探
本质是将参数定义并且放入对应的代码段。在insmod或者modinfo的时候,根据入参去判断params中是否有这个参数,如果有直接修改对应的值。当然还会做一些权限校验。
- 在include/linux/moduleparam.h文件中
module_param(param1, int, S_IRUGO);
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
#define module_param_named(name, value, type, perm) \
param_check_##type(name, &(value)); \
module_param_cb(name, ¶m_ops_##type, &value, perm); \
__MODULE_PARM_TYPE(name, #type)
#define __MODULE_INFO_PREFIX KBUILD_MODNAME "."
#define __MODULE_INFO(tag, name, info) \
static const char __UNIQUE_ID(name)[] \
__used __section(".modinfo") __attribute__((unused, aligned(1))) \
= __MODULE_INFO_PREFIX __stringify(tag) "=" info
#define __MODULE_PARM_TYPE(name, _type) \
__MODULE_INFO(parmtype, name##type, #name ":" _type)
可见使用type在.modinfo的代码段定义了字符串数组列表。也就是modinfo的时候输出的信息。
可见定义了一个module_param_cb的函数,将名字以及比如param_ops_int的处理函数传入(包括地址和权限),然后进行处理。
更多细节:
#define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
/* Default value instead of permissions? */ \
static const char __param_str_##name[] = prefix #name; \
static struct kernel_param __moduleparam_const __param_##name \
__used \
__section("__param") __attribute__ ((unused, aligned(sizeof(void *)))) \
= { __param_str_##name, THIS_MODULE, ops, \
VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }
#define module_param_cb(name, ops, arg, perm) \
__module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)
可见将代码放入__param代码段,并且以__param_str_作为前缀的字符串变量,并且存入这个变量。比如前面就是 char __param_str_param1 = "param1"
,它使用#的宏定义将变量转化为一个字符串。
然后定义了一个struct kernel_param
的数据结构(更多kernel_param数据结构参考后文),并且使用了结构化初始化定义:
将name使用了__param_str_param1
的这个变量,而这个变量的实际值是:“param1”,也就是module_param的第一个参数,然后THIS_MODULE是一个指针指向struct modue
。还有他的操作函数ops有前面可以得知假设是int就是:param_ops_int
的ops,他是一个struct kernel_param_ops 的结构具体参考后文。定义了set get free的回调。
MODULE_PARM_DESC
#define MODULE_PARM_DESC(_parm, desc) \
__MODULE_INFO(parm, _parm, #_parm ":" desc)
比如:MODULE_PARM_DESC(param1, "Debug level (0-2)");
可见在modinfo的代码段添加数组,以param1为前缀转化为字符串 然后添加: 然后添加描述
最终执行的后的效果参考modinfo中的:
struct kernel_param 数据结构
可见以struct kernel_param 为主,包括有name mod、ops以及权限perm还有flags以及他的string和array。
具体结构参考:
struct kernel_param {
const char *name;
struct module *mod;
const struct kernel_param_ops *ops;
const u16 perm;
s8 level;
u8 flags;
union {
void *arg;
const struct kparam_string *str;
const struct kparam_array *arr;
};
};
关于一些函数类型的处理函数 struct kernel_param_ops
extern const struct kernel_param_ops param_ops_int;
extern int param_set_int(const char *val, const struct kernel_param *kp);
extern int param_get_int(char *buffer, const struct kernel_param *kp);
struct kernel_param_ops {
/* How the ops should behave */
unsigned int flags;
/* Returns 0, or -errno. arg is in kp->arg. */
int (*set)(const char *val, const struct kernel_param *kp);
/* Returns length written or -errno. Buffer is 4k (ie. be short!) */
int (*get)(char *buffer, const struct kernel_param *kp);
/* Optional function to free kp->arg when module unloaded. */
void (*free)(void *arg);
};
高阶玩法
比如自定义module_param_cb替换module_param来定义一个struct kernel_param,以及他自己的处理函数nx_huge_pages_ops
。这样就能在初始化的时候能够调用想要处理参数的业务逻辑,提高更多可玩性。但本后的本质还是定义了struct kernel_param_ops。然后这个ops会在
static int __read_mostly nx_huge_pages = -1;
module_param_cb(nx_huge_pages, &nx_huge_pages_ops, &nx_huge_pages, 0644);
__MODULE_PARM_TYPE(nx_huge_pages, "bool");
static const struct kernel_param_ops nx_huge_pages_ops = {
.set = set_nx_huge_pages,
.get = param_get_bool,
};
static int set_nx_huge_pages(const char *val, const struct kernel_param *kp)
{
bool old_val = nx_huge_pages;
bool new_val;
/* In "auto" mode deploy workaround only if CPU has the bug. */
if (sysfs_streq(val, "off"))
new_val = 0;
else if (sysfs_streq(val, "force"))
new_val = 1;
else if (sysfs_streq(val, "auto"))
new_val = get_nx_auto_mode();
else if (strtobool(val, &new_val) < 0)
return -EINVAL;
__set_nx_huge_pages(new_val);
if (new_val != old_val) {
struct kvm *kvm;
mutex_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
mutex_lock(&kvm->slots_lock);
kvm_mmu_zap_all_fast(kvm);
mutex_unlock(&kvm->slots_lock);
wake_up_process(kvm->arch.nx_lpage_recovery_thread);
}
mutex_unlock(&kvm_lock);
}
return 0;
}
int param_get_bool(char *buffer, const struct kernel_param *kp)
{
/* Y and N chosen as being relatively non-coder friendly */
return sprintf(buffer, "%c\n", *(bool *)kp->arg ? 'Y' : 'N');
}
综述
本文介绍了如何在Linux内核模块中添加和设置动态参数,以及如何查看模块参数的方法。包括:
使用module_param宏定义模块参数,可以指定参数类型和权限。
module_param_cb允许定义自定义的处理函数,用于更复杂的参数处理。
MODULE_PARM_DESC用于添加参数描述,这些描述存储在.modinfo代码段。
在加载模块时,可以通过insmod命令指定参数值,如insmod param.ko param1=100。
并且介绍了struct kernel_param和struct kernel_param_ops结构,这些结构用于定义参数和处理函数。