Kernel Parameters
来源:互联网 发布:阿里云服务器在哪买 编辑:程序博客网 时间:2024/04/30 07:12
Linux内核参数是在Linux内核中由宏__setup定义的一系列参数。内核参数包括启动参数和内核模块参数,完整的内核参数列表可以参见Documents/kernel-parameters.txt。
一 关于__setup宏和参数的定义
__setup的定义在include/linux/init.h
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
__setup_param的定义为
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata __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 }
其中使用的 obs_kernel_param结构体为
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
可见,__setup宏的作用是使用str值和函数句柄fn初始化一个static结构体 obs_kernel_param。该结构体在链接后存在于.init.setup段。其实该段也就是所有内核参数所在的处。该段的起始地址是__setup_start,结束地址为__setup_en。
同样的还有一个early_param宏,也是设置一个内核参数,不过改参数是早期启动时相关的。
比如说定义一个内核参数来实现对init程序的指定。见init/main.c中
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
/*
* In case LILO is going to boot us with default command line,
* it prepends "auto" before the whole cmdline which makes
* the shell think it should execute a script with such name.
* So we ignore all arguments entered _before_ init=... [MJ]
*/
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);
二 kernel_param 结构体和参数的存储
内核参数结构体的定义,见include/linux/moduleparam.h
struct kernel_param {
const char *name;
unsigned int perm;
param_set_fn set;
param_get_fn get;
union {
void *arg;
const struct kparam_string *str;
const struct kparam_array *arr;
};
};
其中,联合体内定义的三个成员,第一个其实是字符类型的封装。
struct kparam_string {
unsigned int maxlen;
char *string;
};
第二个是数组类型的封装。
struct kparam_array
{
unsigned int max;
unsigned int *num;
param_set_fn set;
param_get_fn get;
unsigned int elemsize;
void *elem;
};
同时 kernel_param还定义了两个方法,以函数指针存在。分别是设置和读取操作。
/* Returns 0, or -errno. arg is in kp->arg. */
typedef int (*param_set_fn)(const char *val, struct kernel_param *kp);
/* Returns length written or -errno. Buffer is 4k (ie. be short!) */
typedef int (*param_get_fn)(char *buffer, struct kernel_param *kp);
还剩下的常字符串类型成员name为内核参数的名称,而perm为权限????。
kernel_param结构体的初始化方法定义为
/* 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, 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, set, get, { arg } }
和结构体 obs_kernel_param类似,该宏函数保持所有实例存在于__param段。该段的起始地址是__start___param,结束地址为__stop___param。具体链接脚本在include/asm-generic/vmlinux.lds.h
我们也给一个例子(net/ipv4/netfilter/nf_nat_ftp.c)
static int warn_set(const char *val, struct kernel_param *kp)
{
printk(KERN_INFO KBUILD_MODNAME
": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
return 0;
}
module_param_call(ports, warn_set, NULL, NULL, 0);
处理module_param_call之外,还有core_param也可以定义内核参数,不过内核参数不可以模块化,也不可以使用前缀命名(如“printk.”)。
三 内核命令行
内核命令行(command line),是内核参数传递的载体。在init/main.c中定义。
/* Untouched command line saved by arch-specific code. */
char __initdata boot_command_line[COMMAND_LINE_SIZE];
/* Untouched saved command line (eg. for /proc) */
char *saved_command_line;
/* Command line for parameter parsing */
static char *static_command_line;
四 启动时内核参数的处理
内核启动线程start_kernel中,第一次与内核参数有关的操作为
char * command_line;
........
setup_arch(&command_line)
........
该函数是体系结构特有的初始化。定义在arch/<ARCH>/kernel/setup.c中。
对于x86是函数为
void __init setup_arch(char **cmdline_p)
{
.......
#ifdef CONFIG_CMDLINE_BOOL
#ifdef CONFIG_CMDLINE_OVERRIDE
strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
#else
if (builtin_cmdline[0]) {
/* append boot loader cmdline to builtin */
strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE);
strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE);
strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
}
#endif
#endif
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
.......
}
其中builtin_cmdline定义为,其实就是内核编译时指定的内嵌命令行
static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE;
总结以上代码就是:
如果内核支持命令行重载(编译时指定命令行,即内嵌命令行)
复制内嵌命令行到cmdline_p(指向 kernel_start函数中的command_line)
如果内核不支持命令行重载,且内嵌命令行为不为空
合并内嵌命令行和启动命令行,并复制给cmdline_p
对于ARM架构
由于ARM架构对内核参数的传递方法和x86有很大的不同,所以这一部分也和之前有很大的不同。ARM是通过tag链表的方式来传递内核参数的。具体可以参考另一篇Linux Kernel Study : ARM Linux Tag Lists
下一步就是对获得的command_line进行处理了。继续在start_kernel函数中。
......
setup_command_line(command_line);
......
该函数的定义为
static void __init setup_command_line(char *command_line)
{
saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
static_command_line = alloc_bootmem(strlen (command_line)+1);
strcpy (saved_command_line, boot_command_line);
strcpy (static_command_line, command_line);
}
主要是为 saved_command_line和 static_command_line分配内存,备份 boot_command_line到saved_command_line,复制command_line到static_commmand_line。
现在所有命令行都初始化完毕,开始分析命令行并做相应设置。继续在start_kernel函数中。
......
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);
......
在打印内核命令行之后,调用的parse_early_param();其定义如下:
void __init parse_early_param(void)
{
static __initdata int done = 0;
static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
if (done)
return;
/* All fall through to do_early_param. */
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_early_options(tmp_cmdline);
done = 1;
}
void __init parse_early_options(char *cmdline)
{
parse_args("early options", cmdline, NULL, 0, do_early_param);
}
而parse_args函数的原型在include/linux/moduleparam.h,定义在kernel/params.c 。
/* Called on module insert or kernel boot */
extern int parse_args(const char *name,
char *args,
struct kernel_param *params,
unsigned num,
int (*unknown)(char *param, char *val));
在这里parse_early_param()主要的作用是处理内核命令行(boot_command_line)的内核参数。也就是处理在内核命令行中有定义的早期参数值(early=1),特别的还包括内核参数console和earlycon。都和输出流有关,内核启动时的打印信息就要求该设备的正确配置。
之后,第二步是处理其他内核参数(command_line)。
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);
这里主要是根据command_line 的内核参数设置,如过有未知的参数,则作为init的参数传递给init。环境变量存在envp_init数组,命令行存储在argv_init数组。
static void run_init_process(char *init_filename)
{
argv_init[0] = init_filename;
kernel_execve(init_filename, argv_init, envp_init);
}
总结:
1. 对于在bootloader中,通过tag链表传递的参数,在setup_arch()函数中先处理。
2. 对于通过静态编译进内核的启动命令行参数,在setup_arch()函数中处理完tag链表中的参数后,进行处理,但如果在tag链表中也加入了启动命令行,且在arm中,多未对machine_desc->fixup函数未定义,故tag链表中的启动命令行会覆盖静态的命令行,并通过parse_cmdline()进行处理,且所定义的处理函数通过__early_param(name,fn)放置在.early_param.init段中。
3. 对于通过宏early_param(str, fn) 定义的内核参数,其处理函数通过obs_kernel_param结构体,放置在.init.setup段中,且相应earlay字段置1。其处理在函数parse_early_param()被调用。
4. 对于通过宏__setup(str, fn)定义的内核参数,其处理函数也通过obs_kernel_param结构体,放置在.init.setup段中,但相应earlay字段清0。其处理函数通过parse_args()->unknown_bootoption()->obsolete_checksetup()被调用。
5. 对于通过宏core_param(name, var, type, perm)、module_param(name, type, perm)定义的内核内核参数,不同于上面介绍的内核参数,它们是通过定义相应的字符串处理函数来进行相应的处理,不一定对应内核一个固定的变量,而core_param等宏就是定义相应的变量,而这些变量通过parse_args()->unknown_bootoption()进行设置。
注:
对很多参数可通过tag链表、启动命令行等进行设置,参数处理函数也可通过不同的宏置于不同的段中,注意上面的处理顺序,后面的处理会覆盖前面的处理。
- kernel-parameters
- kernel parameters
- Kernel Parameters
- Modifying Linux Kernel Parameters
- Linux Kernel Parameters
- some linux kernel parameters tune
- Linux Kernel Boot Parameters(Linux内核启动参数)
- grub 内核启动参数(kernel command-line parameters)
- insmod: error inserting 'kernel.ko': -1 Inavalid parameters
- systemtap dump kernel function call stack and print function parameters
- Parameters
- Parameters
- Parameters
- parameters
- Nginx优化(配置文件&内核参数)Nginx optimization (Profile & Kernel parameters)
- 10 boot time parameters you should know about the Linux kernel
- Deep Fisher Kernels – End to End Learning of the Fisher Kernel GMM Parameters(泛读)
- The kernel module programming primer(4)-Writing interrupt module with parameters
- PHP array_multisort对多维数组或多个数组排序
- 独立思考锁表了咋整
- 欢迎使用CSDN-markdown编辑器
- C和指针读后笔记
- BNU 13288 Bi-shoe and Phi-shoe(欧拉函数)
- Kernel Parameters
- Android活动生命周期
- 看好你的门-保护数据存储区(3)-XPath注入防御
- scrollview 和listview共存问题
- 【数据结构与算法】二叉树的Java实现及特点总结
- 纯css实现超宽图片全屏居中(兼容淘宝店铺)
- Android 5.1: Unwrapping a new Lollipop update
- Ubuntu下使用iptux
- 算法导论9.3-8 找出已排序的2个数组X[1..n]和Y[1..n]的共2n个数的中位数