1:信息
内核版本: Linux 2.6.22
busybox版本:busybox-1.7.0
2:文件系统的出现位置
板卡上电后先由UBOOT启动初始化办卡,将Linux移到内存中运行
- 由linux内核自行做初始化操作,挂载了第一个应用程序(根文件系统 /linuxrc)
- 根文件系统会提供磁盘管理服务,glic设备节点,配置文件,应用程序 shell命令(Android就是linux多了个文件系统)
文件系统的重要部分:
1:标准库:glibc OpenGl media framwork
2:配置文件:/etc/init.d/rcS (开机运行的软件,显示的画面,执行的命令),/sys/ 开机挂载的设备节点
3:设备节点:/dev/console 控制节点 /dev/null -->mknode
4:架构程序:对多种服务和功能进行系统接口封装
5:shell的实现:所有的shell命令都在文件系统中
根文件系统:类似电脑的C盘:最小的文件系统
微观:
常用的到的一种文件系统: BusyBox 官网:各版本下载:主要实现shell命令
创建一些shell命令,并根据此类的shell命令进行相应的操作
3:文件系统起点
3.1 内核启动的部分代码
start_kernel
-> rest_init
-> init_post
-> run_init_process(execute_command)
//main.c linux-2.6.22.6\init 20513 2007-08-31
asmlinkage void __init start_kernel(void)
{
...
...
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
int pid;
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
...
...
}
static int __init kernel_init(void * unused)
{
...
...
init_post();
return 0;
}
static int noinline init_post(void)
{ if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) //打开控制台作为标准输入
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);//复制,作为标准输出
(void) sys_dup(0);//复制,作为标准错误
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) { //如果命令串被定义执行自定义的命令
run_init_process(execute_command); //run_init_process执行成功会把代码段替换成execute_command对应的内容,下面的代码都会被覆盖掉,就不会执行。如果失败就会执行下面的语句
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
//如果命令串未被定义则进行默认程序执行
run_init_process("/sbin/init"); //同上, 这个有busybox提供,是一个软链接指向busybox
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
//如果当前系统中没有可执行的程序,则内核文件系统启动失败
panic("No init found. Try passing init= option to kernel.");
}
3.2 execute_command的来源
对execute_command 进行赋值
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); //init_setup 由__setup调用,并将函数init_setup放到指定得段位置
3.2.1 __setup的定义
//init.h linux-2.6.22.6\include\linux 10382 2007-08-31
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata = str; \
static struct obs_kernel_param __setup_##unique_id \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
3.2.2 对宏进行代入解析
__setup("init=", init_setup);
__setup_param("init=",init_setup, init_setup, 0)
static char __setup_str_init_setup[] __initdata = "init="; \
static struct obs_kernel_param __setup_init_setup \ //定义结构体,规定一下属性
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \ //属于这个段
__attribute__((aligned((sizeof(long))))) \ //4字节对齐
= { __setup_str_init_setup, init_setup, 0}
下面是段定义的位置
//vmlinux.lds.S linux-2.6.22.6\arch\arm\kernel 3724 2007-08-31
.init : { /* Init code and data */
__setup_start = .;
*(.init.setup)
__setup_end = .;
表示上面的结构体 obs_kernel_param存放在".init.setup"段中,每个结构体包括下面3个部分
struct obs_kernel_param {
const char *str; //字符串
int (*setup_func)(char *); //函数
int early; //标志位
};
3.3 该结构体使用位置
3.3.1 p->early 等于0 时调用的函数
参数是一个整体。例如line 是 “init = xxxxx”
//main.c linux-2.6.22.6\init 20513 2007-08-31
static int __init obsolete_checksetup(char *line)
{
struct obs_kernel_param *p;
int had_early_param = 0;
p = __setup_start; //p指向*(.init.setup)的开头
//对p从开头到结尾进行遍历,
do {
int n = strlen(p->str);
if (!strncmp(line, p->str, n)) { //如果遍历中p的字符串和传进来的line相等
if (p->early) { //如果p->early 不等于0,
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '\0' || line[n] == '=')
had_early_param = 1;
} else if (!p->setup_func) {//如果对应的函数p->setup_func为NULL,打印错误信息
printk(KERN_WARNING "Parameter %s is obsolete,"
" ignored\n", p->str);
return 1;
} else if (p->setup_func(line + n))//如果p->early 等于0 ,n是结构体中已存在的字符串的长度("init ="的长度),
//line指向的字符串是这样的 "init = xxxxx" 那么line + n 就会指向" xxxxx", 目的就是提取字符串的参数部分
return 1;
}
p++;
} while (p < __setup_end);
return had_early_param;
}
调用的顺序
start_kernel ->parse_args->unknown_bootoption->obsolete_checksetup
asmlinkage void __init start_kernel(void)
{
//parse_args会对传进来参数static_command_line进行处理,init = xxxxx会被处理成xxxxx,然后传递到unknown_bootoption中去
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
}
static int __init unknown_bootoption(char *param, char *val)
{
/* Handle obsolete-style parameters */
if (obsolete_checksetup(param))
return 0;
}
3.3.2 p->early 不等于0 时调用的函数
参数是分成两部分。例如"init = xxxxx" (param 是 "init = " ) ( val是 “xxxxx”)
//main.c linux-2.6.22.6\init 20513 2007-08-31
/* 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) { //p->early为1 并且传进来的param和p-str相同
if (p->setup_func(val) != 0)
printk(KERN_WARNING
"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
这里的p->setup_func(val)
就是前面往结构体中存放的函数 static int __init init_setup(char *str)
(对execute_command 进行赋值)
uboot传入得的以init = xxxxx的参数, 会 通过"init"在固定端位置遍历结构体,等找到对应的结构体,然后利用这个这个的函数setup_func,把参数xxxx放到这个函数中p->setup_func(xxxxx)
,这个函数中会对execute_command 赋值execute_command = xxxxx
3.4 总结
U-boot传递了很多参数,taglist
被解析成很多的setup段,这些段都存放在.ini.setup的代码段中,形式为CMD(字符串)命令对应得处理函数
在两个函数 obsolete_checksetup(处理early = 0) 和do_early_param(处理early != 0)中进行了所有存放在.init.setup代码段得命令执行
针对setup段得CMD(execute_command)进行全局变量得赋值
execute_command 时uboot传入得的以init = xxxxx的参数
例如uboot传入得command得参数 init = linuxrc 那么execute_command = linuxrc
针对3.1 的代码
if (execute_command) {
run_init_process(execute_command);
可以转化为
if (linuxrc) {
run_init_process(linuxrc); 内核切换到文件系统的中进行linuxrc应用程序的运行
linuxrc指向busybox
如果没有定义linuxrc 就会运行"/sbin/init"
run_init_process("/sbin/init"); //同上, 这个有busybox提供,是一个软链接指向busybox
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
"/sbin/init"也指向busybox