我们知道内核启动后就要挂载根文件系统再执行应用程序,下面来分析一下根文件系统的流程,和所需要的东西。
内核挂接根文件系统后,怎么样启动第一个应用程序呢?内核分析得到 执行 static int __init kernel_init(void * unused) 分析这个函数
kernel_init(void * unused)
开始一堆初始化后。。。。打开一个设备
先打开一个设备 /dev/console 就是串口0终端,后面两个 sys_dup(0); 复制两个设备,三个设备是指标准输入 标准输出 标准错误 指从那输入 从那输出,
对于其它的设备 这个console 可能是键盘 LCD等 接下来调用 static noinline int init_post(void)
init_post(void)
如果定义了 execute_commadn 就会调用 run_init_process(execute_commadn); execute_commadn 是命令行参数 init=/linuxrc 如果有定义就会执行。
未定义就往下执行 一但执行 run_init_process 后,如果不出错或退出,就会一直在这个函数内执行,不会返回了。我们分析 run_init_process 这个函数就能知道,他需要那些内容
分析busybox源码来进一步分析 在busybox源码下找到 我们的init程序 里面会有init.c 里面有init_main(init argc,char **argv); 从这里开始分析
init_main(init argc,char **argv)
一些设置和初始化后
上面 run_init_process 我们没有传进参数 所以执行else分支 parse_inittab 解析配置
static void parse_inittab(void)
parser_t *parser = config_open2("/etc/inittab", fopen_for_read); //打开 /etc/inittab 配置文件一般都放在 /etc 目录下
如果配置文件不存在 就会执行 new_init_action 这个函数就是创建黙认的配置文件 下面来分析配置文件
在busybox 里inittab的说明 去查看一下 得到格式是
Format for each entry: <id>:<runlevels>:<action>:<process>
<id> : /dev/id 用于终端
<runlevels> : 完全可以忽略
<action> : 执行时机
<process> : 就用程序或脚本
分析 static void new_init_action(uint8_t action_type, const char *command, const char *cons)
创建struct init_action *a, **nextp; 结构体 填充 用传入的参数填充 init_action有下面的成员
struct init_action {
struct init_action *next;
pid_t pid;
uint8_t action_type;
char terminal[CONSOLE_NAME_SIZE];
char command[1];
};
nextp = &init_action_list; 链表
如果有配置文件 就解析它,如果没有就新建一个,我们用代码反推出一个配置文件,
<id>:<runlevels>:<action>:<process> 格式
/* No inittab file - set up some default behavior */
/* Sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, ""); # define INIT_SCRIPT "/etc/init.d/rcS"
::sysinit:/etc/init.d/rcS
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, ""); const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; #define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh"
::askfirst:-/bin/sh
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
/dev/tty2::askfirst:-/bin/sh
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
/dev/tty3::askfirst:-/bin/sh
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/dev/tty4::askfirst:-/bin/sh
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
::ctrlaltdel:reboot
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
::shutdown:umount -a -r
/* Swapoff on halt/reboot */
new_init_action(SHUTDOWN, "swapoff -a", "");
::shutdown:swapoff -a
/* Restart init when a QUIT is received */
new_init_action(RESTART, "init", "");
::restart:init
return;
得到配置文件如下:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
/dev/tty2::askfirst:-/bin/sh
/dev/tty3::askfirst:-/bin/sh
/dev/tty4::askfirst:-/bin/sh
::ctrlaltdel:reboot
::shutdown:umount -a -r
::shutdown:swapoff -a
::restart:init
解析配置文件后面就是执行 如下
run_actions 执行某一类程序 根据参数
sysinit wait ctrlaltdel shutdown 这些类 等待执行完毕 然后杀掉,
once 这类不等待执行完毕
respawn askfirst 重复执行,通过PID来决定何时执行。这两个区别就是 askfirst 需要在启动时 输入回车才会接着运行。把askfirst 改成respawn 就可以直接进入终端,不需要按回车键
通过上面的分析,得到以下信息,
1:/dev/console
2:busybox
3:配置文件/etc/inittab
4:C库 就用程序是C库实现的
5:配置文件里指定的应用程序
6:补充 /dev/null 当ID为空时,就定位到这个设备