我们先看一个shell脚本,安装模块:
#########
insmod net_dev.ko param_queue_id=${QUEUE_ID} param_board=$BOARD \
param_tcont_id=$TCONT_ID param_us_gem_flow_id=$US_GEMFLOW_ID
真正代码层:
static int param_queue_id =3;
module_param(param_queue_id, int, 0);
int map_ds_traffic_to_cpu = 0;
module_param(map_ds_traffic_to_cpu, int, 0);
static char param_board[20]="";
module_param_string(param_board, param_board, sizeof(param_board), 0);
//
参数用 module_param 宏定义来声明, 它定义在 moduleparam.h.
module_param(name,type,perm);
module_param 使用了 3 个参数: 变量名, 它的类型, 以及一个权限掩码用来做一个辅助的 sysfs 入口(查看/sys/module/xXX/会发现里面有我们对应的参数,当然必须有至少读的权限,默认0,为任何人都没有权限,甚至不可见). 这个宏定义应当放在任何函数之外, 典型地是出现在源文件的前面.
数组参数, 用逗号间隔的列表提供的值, 模块加载者也支持. 声明一个数组参数, 使用:
module_param_array(name,type,num,perm);
这里 name 是你的数组的名称(也是参数名),
type 是数组元素的类型,
num 是一个整型变量,
perm 是通常的权限值.
如果数组参数在加载时设置, num 被设置成提供的数的个数. 模块加载者拒绝比数组能放下的多的值. module_param_array() 宏的第三个参数用来记录用户insmod 时提供的给这个数组的元素个数,NULL 表示不关心用户提供的个数
如果模块参数是一个字符串时,通常使用charp类型定义这个模块参数。内核复制用户提供的字符串到内存,并且相对应的变量指向这个字符串。
例如:
static char *name;
module_param(name, charp, 0);
另一种方法是通过宏module_param_string()让内核把字符串直接复制到程序中的字符数组内。
module_param_string(name, string, len, perm);
这里,name是外部的参数名,string是内部的变量名,len是以string命名的buffer大小(可以小于buffer的大小,但是没有意义),perm表示sysfs的访问权限(或者perm是零,表示完全关闭相对应的sysfs项)。
例如:
static char species[BUF_LEN];
module_param_string(specifies, species, BUF_LEN, 0);
如果需要传递多个参数可以通过宏module_param_array()实现。
module_param_array(name, type, nump, perm);
其中,name既是外部模块的参数名又是程序内部的变量名,type是数据类型,perm是sysfs的访问权限。指针nump指向一个整数,其值表示有多少个参数存放在数组name中。值得注意是name数组必须静态分配.
说了这么多,来让我们看看它的本质吧.
include/linux/moduleparam.h
点击(此处)折叠或打开
- /* This is the fundamental function for registering boot/module
- parameters. perm sets the visibility in sysfs: 000 means it's
- not there, read bits mean it's readable, write bits mean it's
- writable. */
- #define __module_param_call(prefix, name, set, get, arg, isbool, perm) \
- /* Default value instead of permissions? */ \
- static int __param_perm_check_##name __attribute__((unused)) = \
- BUILD_BUG_ON_ZERO((perm) 0 || (perm) > 0777 || ((perm) & 2)) \
- + BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN); \
- 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, perm, isbool ? KPARAM_ISBOOL : 0, \
- set, get, { arg } }
-
- #define module_param_call(name, set, get, arg, perm) \
- __module_param_call(MODULE_PARAM_PREFIX, \
- name, set, get, arg, \
- __same_type(*(arg), bool), perm)
-
- /* Helper functions: type is byte, short, ushort, int, uint, long,
- ulong, charp, bool or invbool, or XXX if you define param_get_XXX,
- param_set_XXX and param_check_XXX. */
- #define module_param_named(name, value, type, perm) \
- param_check_##type(name, &(value)); \
- module_param_call(name, param_set_##type, param_get_##type, &value, perm); \
- __MODULE_PARM_TYPE(name, #type)
-
- #define module_param(name, type, perm) \
- module_param_named(name, name, type, perm)
那么我们看下 struct kernel_param:
点击(此处)折叠或打开
- struct kernel_param {
- const char *name;
- u16 perm;
- u16 flags;
- param_set_fn set;
- param_get_fn get;
- union {
- void *arg;
- const struct kparam_string *str;
- const struct kparam_array *arr;
- };
- };
-
- /* Special one for strings we want to copy into */
- struct kparam_string {
- unsigned int maxlen;
- char *string;
- };
-
- /* Special one for arrays */
- struct kparam_array
- {
- unsigned int max;
- unsigned int *num;
- param_set_fn set;
- param_get_fn get;
- unsigned int elemsize;
- void *elem;
- };
点击(此处)折叠或打开
- /* This is where the real work happens */
- SYSCALL_DEFINE3(init_module, void __user *, umod,
- unsigned long, len, const char __user *, uargs)
- {
- struct module *mod;
- int ret = 0;
-
- /* Must have permission */
- if (!capable(CAP_SYS_MODULE) || modules_disabled)
- return -EPERM;
-
- /* Only one module load at a time, please */
- if (mutex_lock_interruptible(&module_mutex) != 0)
- return -EINTR;
-
- /* Do all the hard work */
- mod = load_module(umod, len, uargs);
- if (IS_ERR(mod)) {
- mutex_unlock(&module_mutex);
- return PTR_ERR(mod);
- }
-
- /* Drop lock so they can recurse */
- mutex_unlock(&module_mutex);
-
- blocking_notifier_call_chain(&module_notify_list,
- MODULE_STATE_COMING, mod);
-
- do_mod_ctors(mod);
- /* Start the module */
- if (mod->init != NULL)
- ret = do_one_initcall(mod->init);
- if (ret 0) {
- /* Init routine failed: abort. Try to protect us from
- buggy refcounters. */
- mod->state = MODULE_STATE_GOING;
- synchronize_sched();
- module_put(mod);
- blocking_notifier_call_chain(&module_notify_list,
- MODULE_STATE_GOING, mod);
- mutex_lock(&module_mutex);
- free_module(mod);
- mutex_unlock(&module_mutex);
- wake_up(&module_wq);
- return ret;
- }
- if (ret > 0) {
- printk(KERN_WARNING
- "%s: '%s'->init suspiciously returned %d, it should follow 0/-E convention\n"
- "%s: loading module anyway...\n",
- __func__, mod->name, ret,
- __func__);
- dump_stack();
- }
-
- /* Now it's a first class Wake up anyone waiting for it. */
- mod->state = MODULE_STATE_LIVE;
- wake_up(&module_wq);
- blocking_notifier_call_chain(&module_notify_list,
- MODULE_STATE_LIVE, mod);
-
- /* We need to finish all async code before the module init sequence is done */
- async_synchronize_full();
-
- mutex_lock(&module_mutex);
- /* Drop initial reference. */
- module_put(mod);
- trim_init_extable(mod);
- #ifdef CONFIG_KALLSYMS
- mod->num_symtab = mod->core_num_syms;
- mod->symtab = mod->core_symtab;
- mod->strtab = mod->core_strtab;
- #endif
- module_free(mod, mod->module_init);
- mod->module_init = NULL;
- mod->init_size = 0;
- mod->init_text_size = 0;
- mutex_unlock(&module_mutex);
-
- return 0;
- }
查看模块信息:
modinfo [-adhpv][模块文件]
功能:显示kernel模块的信息。
参数:
-a或--author 显示模块开发人员。
-d或--description 显示模块的说明。
-h或--help 显示modinfo的参数使用方法。
-p或--parameters 显示模块所支持的参数。
-V或--version 显示版本信息。
既然上面说到了模块参数,这里顺便也说下
引导期间内核选项 __setup
用__setup宏注册关键字
net/core/dev.c 比较常见的netdev关键字 ,在系统启动时候的参数
点击(此处)折叠或打开
- /*
- * Saves at boot time configured settings for any netdevice.
- */
- int __init netdev_boot_setup(char *str)
- {
- int ints[5];
- struct ifmap map;
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
- if (!str || !*str)
- return 0;
-
- /* Save settings */
- memset(&map, 0, sizeof(map));
- if (ints[0] > 0)
- map.irq = ints[1];
- if (ints[0] > 1)
- map.base_addr = ints[2];
- if (ints[0] > 2)
- map.mem_start = ints[3];
- if (ints[0] > 3)
- map.mem_end = ints[4];
-
- /* Add new entry to the list */
- return netdev_boot_setup_add(str, &map);
- }
-
- __setup("netdev=", netdev_boot_setup);
点击(此处)折叠或打开
- static int __init root_dev_setup(char *line)
- {
- strlcpy(saved_root_name, line, sizeof(saved_root_name));
- return 1;
- }
-
- __setup("root=", root_dev_setup);
点击(此处)折叠或打开
- static int __init readonly(char *str)
- {
- if (*str)
- return 0;
- root_mountflags |= MS_RDONLY;
- return 1;
- }
-
- static int __init readwrite(char *str)
- {
- if (*str)
- return 0;
- root_mountflags &= ~MS_RDONLY;
- return 1;
- }
-
- __setup("ro", readonly);
- __setup("rw", readwrite);
我们看看它是如何调用的.
在init/main.c 中
asmlinkage void __init start_kernel(void)
{
...
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
...
} 这里我们看到启动的时候传递的参数会在这里解析。我们继续回到init.h
点击(此处)折叠或打开
- struct obs_kernel_param {
- const char *str;
- int (*setup_func)(char *);
- int early;
- };
-
- /*
- * Only for really core code. See moduleparam.h for the normal way.
- *
- * Force the alignment so the compiler doesn't space elements of the
- * obs_kernel_param "array" too far apart in .init.setup.
- */
- #define __setup_param(str, unique_id, fn, early) \
- static const char __setup_str_##unique_id[] __initconst \
- __aligned(1) = str; \
- static struct obs_kernel_param __setup_##unique_id \
- __used __section(.init.setup) \
- __attribute__((aligned((sizeof(long))))) \
- = { __setup_str_##unique_id, fn, early }
-
- #define __setup(str, fn) \
- __setup_param(str, fn, fn, 0)
-
- /* NOTE: fn is as per module_param, not Emits warning if fn
- * returns non-zero. */
- #define early_param(str, fn) \
- __setup_param(str, fn, fn, 1)
注册的时候有两个宏,__setup, early_param 当然既然带个early自然会比__setup注册的处理的早. 也就是我们看到start_kernel里两个函数的先后顺序.
我们就把宏展开:
本质是注册了struct obs_kernel_param 并把它放到了内存节区 .init.setup ,关于内存节区前面我们讲过。当然如果注册的关键字被编译成模块,那么这个宏扩展为空.
我们先看early解析:
点击(此处)折叠或打开
- /* Check for early params. */
- static int __init do_early_param(char *param, char *val)
- {
- struct obs_kernel_param *p;
-
- for (p = __setup_start; p __setup_end; p++) {
- if ((p->early && strcmp(param, p->str) == 0) ||
- (strcmp(param, "console") == 0 &&
- strcmp(p->str, "earlycon") == 0)
- ) {
- if (p->setup_func(val) != 0)
- printk(KERN_WARNING
- "Malformed early option '%s'\n", param);
- }
- }
- /* We accept everything at this stage. */
- return 0;
- }
点击(此处)折叠或打开
- #define INIT_SETUP(initsetup_align) \
- . = ALIGN(initsetup_align); \
- VMLINUX_SYMBOL(__setup_start) = .; \
- *(.init.setup) \
- VMLINUX_SYMBOL(__setup_end) = .;
那么正常注册的关键字呢?
点击(此处)折叠或打开
- parse_args("Booting kernel", static_command_line, __start___param,
- __stop___param - __start___param,
- &unknown_bootoption);
include/asm-generic/vmlinux-lds.h :
点击(此处)折叠或打开
- /* Built-in module parameters. */ \
- __param : AT(ADDR(__param) - LOAD_OFFSET) { \
- VMLINUX_SYMBOL(__start___param) = .; \
- *(__param) \
- VMLINUX_SYMBOL(__stop___param) = .; \
- . = ALIGN((align)); \
- VMLINUX_SYMBOL(__end_rodata) = .;
这里查询__param ,如果找到赋值并返回.
点击(此处)折叠或打开
- /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
- int parse_args(const char *name,
- char *args,
- struct kernel_param *params,
- unsigned num,
- int (*unknown)(char *param, char *val))
- {
- char *param, *val;
-
- DEBUGP("Parsing ARGS: %s\n", args);
-
- /* Chew leading spaces */
- while (isspace(*args))
- args++;
-
- while (*args) {
- int ret;
- int irq_was_disabled;
-
- args = next_arg(args, ¶m, &val);
- irq_was_disabled = irqs_disabled();
- ret = parse_one(param, val, params, num, unknown);
- if (irq_was_disabled && !irqs_disabled()) {
- printk(KERN_WARNING "parse_args(): option '%s' enabled "
- "irq's!\n", param);
- }
- switch (ret) {
- case -ENOENT:
- printk(KERN_ERR "%s: Unknown parameter `%s'\n",
- name, param);
- return ret;
- case -ENOSPC:
- printk(KERN_ERR
- "%s: `%s' too large for parameter `%s'\n",
- name, val ?: "", param);
- return ret;
- case 0:
- break;
- default:
- printk(KERN_ERR
- "%s: `%s' invalid for parameter `%s'\n",
- name, val ?: "", param);
- return ret;
- }
- }
-
- /* All parsed OK. */
- return 0;
- }