1 引言

Linux文件系统和内核是相对独立的,一个内核可以安装不同的文件系统,那么文件系统与内核的衔接就变得十分重要,Linux内核是通过什么方式与各种文件系统进行衔接的?他们的工作流程是怎样的,这就是这篇文章想要分析的问题。

2 文件系统的工作流程

文件系统这个相对独立的部分有他自己的工作流程,他的起点是什么,目的是什么,这是这一章节希望理清楚的部分。

2.1 起点分析

所谓的起点,就是内核启动文件系统的代码了,以linux kernel 2.6.22的内核代码为例,主要的部分如下面所示。

相关代码1:

在文件系统的代码中,这里为起点,这是linux kernel 2.6.22的main.c中的一段代码

1
2
3
4
5
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);

可以看到,打开了一个/dev/console,这是文件系统里面的终端,也是一个句柄,然后复制了两次句柄。
这些代码的实际功能是打开了控制台,又复制了标准输出,复制了标准错误。

相关代码2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 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);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
如果未定义,则进行默认程序执行
run_init_process("/sbin/init");
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.");

上面这段代码也是linux内核main.c里面的,主要内容是要启动初始化程序,用来初始化文件系统,也就是那些个init程序了,这些程序都放在了文件系统里面。在启动根文件系统的时候,要运行这些应用程序,就是双引号里面的那些,很明显,根据这些目录的写法,可以看出这些东西的代码都放在了文件里面。如果文件系统里面没有这些东西,就会报错,也就是panic里面的提示信息。
要注意,那些init,sh程序,都是文件系统提供的,并不是linux内核。

相关代码3:

———————————————–updating—————————–

代码2的execute_command的定义在这里面,也是main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);

从上面的代码看出,execute_command这个字符串只有在init_setup函数中才会被赋值,而谁调用了这个函数呢,就是下面的那一句,
一层一层的分析下去这个宏定义,会发现这个作用是:

总结:

uboot向内核传入了很多的参数,被封装在了taglist里面,被内核解析成了多了setup的段,存放在.init.setupd的代码段中,形式未cmd(字符串)。

而命令对应的处理函数

obsolete_checksetup 处理early == 0 的情况

do_early_param 处理early != 0 的情况

中进行了所有存放在.init.setup代码段的命令的执行

针对各种setup段的cmd进行全局变量execute_command的赋值

这个execute_command就是uboot传入的以init=XXXX的参数,

Uboot传入的cmd参数

init=linuxrc

execute_command=linuxrc

也就是说代码2的第7行会变成这样

1
2
3
if(linuxrc){
run_init_process(linuxrc); //内核切换到了文件系统busybox中进行linuxrc应用程序的运行
}

相关代码4
至此,执行到了文件系统里面,文件系统的相关代码,不在内核中了,需要下载busybox等文件系统查看

3 总结与问题

3.1 总结

uboot向内核传递了大量的参数,封装在了taglist里面,而内核首先就是解析出来这些个参数,把他们都存放到了全局变量里面,在需要的时候使用这些参数,比如初始化的时候。

以上就是uboot这部分和内核的沟通模式,而内核与文件系统的沟通模式也是类似的,

3.2 问题

1. 文件系统进入后,内核运行了什么应用程序?
2. 文件系统的初始化模式是否和内核是一样的?

接收配置的传入

传入参数的格式是什么样的

解析配置信息

如何进行解析,并且进行识别的

应用配置信息

在什么地方用,怎么用这些信息