先看一个具体的例子:
demo.c
Makefile:
编译后:
验证:
依次执行
可以看到通过命令行成功的将参数传递给了ko模块对应的变量。稍后将会知道,内核模块加载器件对模块参数的初始化过程发生在对模块初始化函数init的调用之前。所以在demo_init函数被调用时,已经可以得到从命令行传递过来的参数。
分析:
这一切是如何发生的呢,我们从module_param开始倒查,试图找到整条调用链条,解释整个发生过程。
module_param->module_param_named
之后调用param_check_int进行参数检查
module_param_cb->__module_param_call
可以看到, 最终定义了一个static struct kernel_param 的对象结构体,并通过__section__ ("__param")塞到了__param段里,这里面有一个很重要的成员ops,它来源于module_parm_named定义。
param_ops_##type的定义如下,它的定义形式是通过预处理连接符定义的。
所有对参数的操作都是通过param_set/param_get_##type函数来进行的。
调用链:
前面知道了模块参数的定义是通过宏的精巧使用,构造了一个包含参数地址和参数操作函数的对象,并利用编译器的特性,将对象放到了特定的段中,但是我们还不知道这些定义好的段是如何发挥作用的。核心函数在parse_args中。
通过上面查找,我们看到了主要有两个调用链 调用了parse_arg函数,第一个是从start_kernel发起,在系统初始化阶段调用每个模块的参数初始化函数。
另一个则是在load_module函数中,它是在insmod操作的调用里面被调用的。
总结
所以我们可以得出结论,无论是模块builtin编译,还是以KO模块的形式集成到系统,module_parm宏定义的参数都会发生作用,也就是说,我们可以通过 "paramxxx=xxx"的形式,在系统初始化参数,或者在模块安装的时候,向模块传递参数。