内核模块参数
内核模块参数
背景
接上一篇 内核启动参数cmdline
上一篇主要讨论了不是模块的情况下,内核启动参数的传递解析过程及使用:early_param()
和__setup()
,这篇主要讨论下模块的情况
相关内核文档:Documentation/admin-guide/kernel-parameters.rst
等
内核模块添加参数
module_param(参数名, 参数类型, 0644);
MODULE_PARM_DESC(参数名, "参数说明.");
内核模块参数使用
分3种方式:
- 内核启动cmdline
模块名.参数=xxx
- 模块加载cmdline
modprobe 模块名 参数=xxx
- 动态修改
/ sys / module / <模块名> / parameters/参数
例如:
(kernel command line) usbcore.blinkenlights=1
(modprobe command line) modprobe usbcore blinkenlights=1
内核模块定义和解析过程
定义
下面是module_param
定义(include/linux/moduleparam.h
):
module_param
-> module_param_named
-> module_param_cb
-> __module_param_call
/**
* module_param - typesafe helper for a module/cmdline parameter
* @value: the variable to alter, and exposed parameter name.
* @type: the type of the parameter
* @perm: visibility in sysfs.
*
* @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
* ".") the kernel commandline parameter. Note that - is changed to _, so
* the user can use "foo-bar=1" even for variable "foo_bar".
*
* @perm is 0 if the the variable is not to appear in sysfs, or 0444
* for world-readable, 0644 for root-writable, etc. Note that if it
* is writable, you may need to use kernel_param_lock() around
* accesses (esp. charp, which can be kfreed when it changes).
*
* The @type is simply pasted to refer to a param_ops_##type and a
* param_check_##type: for convenience many standard types are provided but
* you can create your own by defining those variables.
*
* Standard types are:
* byte, short, ushort, int, uint, long, ulong
* charp: a character pointer
* bool: a bool, values 0/1, y/n, Y/N.
* invbool: the above, only sense-reversed (N = true).
*/
#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_param_cb(name, ops, arg, perm) \
__module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)
/* This is the fundamental function for registering boot/module
parameters. */
#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 \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
= { __param_str_##name, THIS_MODULE, ops, \
VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }
这里以下面代码为例:
module_param(xxxxx int, 0444);
最后展开为:
param_check_int(xxxxx, &(xxxxx)); \
static const char __param_str_xxxxx[] = prefix xxxxx; \
static struct kernel_param __moduleparam_const __param_xxxxx \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
= {
__param_str_xxxxx,
THIS_MODULE,
¶m_ops_int, \
VERIFY_OCTAL_PERMISSIONS(0444),
-1,
0,
{ &xxxxx }
} \
__MODULE_PARM_TYPE(xxxxx, int)
主要定义了struct kernel_param
类型的数据存放在__param
段
struct kernel_param
(include/linux/moduleparam.h
)类型定义为:
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;
};
};
解析过程
在load模块的时候会去查找对应的段,并将各个参数解析处理(kernel/module.c
):
insmod/modprobe 模块
-> sys_init_module() //系统调用
-> load_module() //加载模块
-> find_module_sections() // 查找相应的段 `__param`段保存在mod->kp
-> parse_args() //解析内核参数, 这里开始就和内核启动cmdline处理一致了
-> parse_one() //一个个解析参数,并与用户传进来的参数匹配,成功就设置模块内核参数
我们看下比较核心的 parse_one()
函数的部分代码:
/* Find parameter */
for (i = 0; i < num_params; i++) {
if (parameq(param, params[i].name)) {
if (params[i].level < min_level
|| params[i].level > max_level)
return 0;
/* No one handled NULL, so do it here. */
if (!val &&
!(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
return -EINVAL;
pr_debug("handling %s with %p\n", param,
params[i].ops->set);
kernel_param_lock(params[i].mod);
if (param_check_unsafe(¶ms[i]))
err = params[i].ops->set(val, ¶ms[i]); /* 参数匹配成功就会调用 struct kernel_param 结构的ops函数集,就是对应上面定义的param_ops_int*/
else
err = -EPERM;
kernel_param_unlock(params[i].mod);
return err;
}
}
因为这里参数的类型为int, 所以参数的操作集为内核自带的param_ops_int
,主要就是set(param_set_int
)和get(param_get_int
)的实现,
也可以使用module_param_cb()
自定义参数的操作集,这里就不展开说了。
param_set_int
和param_get_int
主要使用STANDARD_PARAM_DEF
宏(kernel/params.c
)来定义的,这里也不多说了
参考
- 内核源码
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 DD'Notes!
评论