当我们启动了开发板后进入了文件系统,执行ls命令我们可以看到很多目录,在目录下有很多的程序。
ls cd 等等这些也是一个程序,如果我们想要用这些程序的话难道需要一个的找来编译后吗?很明显是不要的,那根文件系统里面怎么就有这些程序的,
那就要我们构造跟文件系统了,随之引入了busybox,他就是ls cp cd等命令的组合。
输入ls -l /bin/ls 会显示/bin/ls ->busybox的链接。
内核的最终目的是启动应用程序,在上一节中我们知道内核在启动第一个程序是run_init_process(“参数”);,我们以启动run_init_process("/sbin/init"); init程序为例(sbin下的init,init还可能在其他目录程序bin等等):
其中init程序是在busybox中的,我们busybox解压,建立source insight工程看源代码,该源码的文件名为init.c
在分析之前可以看看inittab(在buysbox源码目录下搜(example/inittab))
inittab格式 :Format for each entry: <id>:<runlevels>:<action>:<process>
根据这个example/inittab我们可以解读出:
<id>: WARNING: This field has a non-traditional meaning for BusyBox init!
#
# The id field is used by BusyBox init to specify the controlling tty for
# the specified process to run on. The contents of this field are
# appended to "/dev/" and used as-is. There is no need for this field to
# be unique, although if it isn't you may have strange results. If this
# field is left blank, it is completely ignored. Also note that if
# BusyBox detects that a serial console is in use, then all entries
# containing non-empty id fields will be ignored. BusyBox init does
# nothing with utmp. We don't need no stinkin' utmp.
<id>=>/dev/id ,用作终端: stdin stdont 等
<runlevels>: The runlevels field is completely ignored.//这个可以完全忽略。
<action>: Valid actions include: sysinit, respawn, askfirst, wait, once, //何时执行
# restart, ctrlaltdel, and shutdown.
#
# Note: askfirst acts just like respawn, but before running the specified
# process it displays the line "Please press Enter to activate this
# console." and then waits for the user to press enter before starting
# the specified process.
#
# Note: unrecognised actions (like initdefault) will cause init to emit
# an error message, and then go along with its business.
#
# <process>: Specifies the process to be executed and it's command line.//应用程序或脚本
#
# Note: BusyBox init works just fine without an inittab. If no inittab is
# found, it has the following default behavior:
# ::sysinit:/etc/init.d/rcS
# ::askfirst:/bin/sh
# ::ctrlaltdel:/sbin/reboot
# ::shutdown:/sbin/swapoff -a
# ::shutdown:/bin/umount -a -r
# ::restart:/sbin/init
#
# if it detects that /dev/console is _not_ a serial console, it will
# also run:
busybox->init_main //init程序。
parse_inittab
file = fopen(INITTAB, "r") //#define INITTAB "/etc/inittab" 打开配置文件(配置文件启动不同的文件)
if (file == NULL) //如果配置文件打开失败的话就执行默认选项
{
/* No inittab file -- set up some default behavior */
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
/* Swapoff on halt/reboot */
if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
/* Prepare to restart init when a HUP is received */
new_init_action(RESTART, "init", "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
return;
}
接下来要是没有使用默认项,那就解析inittab文件后执行new_init_action。
分析下new_init_action函数。
结构体
struct init_action
{
struct init_action *next;
int action;
pid_t pid;
char command[INIT_BUFFS_SIZE];
char terminal[CONSOLE_NAME_SIZE];
};
static struct init_action *init_action_list = NULL;
new_init_action (int action, const char *command, const char *cons)
{
struct init_action *new_action, *a, *last;
if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
return;
/* Append to the end of the list */
for (a = last = init_action_list; a; a = a->next) {
/* don't enter action if it's already in the list,
* but do overwrite existing actions */
if ((strcmp(a->command, command) == 0)
&& (strcmp(a->terminal, cons) == 0)
) {
a->action = action;
return;
}
last = a;
}
new_action = xzalloc(sizeof(struct init_action));
if (last) {
last->next = new_action;
} else {
init_action_list = new_action;
}
strcpy(new_action->command, command);
new_action->action = action;
strcpy(new_action->terminal, cons);
messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
new_action->command, new_action->action, new_action->terminal);
}
new_init_action 函数的功能就是船创建init_action 结构体并将传入的参数存入结构体init_action ,并将结构体挂在链表上。
接下来假装没有配置文件反推下默认选项,我们知道inittab格式 :Format for each entry: <id>:<runlevels>:<action>:<process>。,
<action>: Valid actions include: sysinit, respawn, askfirst, wait, once, restart, ctrlaltdel, and shutdown. //何时执行
/* 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 */
if (ENABLE_SWAPONOFF)
new_init_action(SHUTDOWN, "swapoff -a", "");
::shutdown:swapoff -a
/* Prepare to restart init when a HUP is received */
new_init_action(RESTART, "init", "");
::restart:init
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
tty2 ::askfirst:-/bin/sh
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
tty3 ::askfirst:-/bin/sh
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
tty4 ::askfirst:-/bin/sh
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
::sysinit:/etc/init.d/rcS
return;
static void run_actions(int action)
{
struct init_action *a, *tmp;
for (a = init_action_list; a; a = tmp) {
tmp = a->next;
if (a->action == action) {
/* a->terminal of "" means "init's console" */
if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
delete_init_action(a);
} else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
waitfor(a, 0);
delete_init_action(a);
} else if (a->action & ONCE) {
run(a);
delete_init_action(a);
} else if (a->action & (RESPAWN | ASKFIRST)) {
/* Only run stuff with pid==0. If they have
* a pid, that means it is still running */
if (a->pid == 0) {
a->pid = run(a);
}
}
}
}
}
run_actions的作用是Run all commands of a particular type。
static int waitfor(const struct init_action *a, pid_t pid)
{
int runpid;
int status, wpid;
runpid = (NULL == a)? pid : run(a);
while (1) {
wpid = waitpid(runpid, &status, 0);
if (wpid == runpid)
break;
if (wpid == -1 && errno == ECHILD) {
/* we missed its termination */
break;
}
/* FIXME other errors should maybe trigger an error, but allow
* the program to continue */
}
return wpid;
}
run_actions(SYSINIT);
waitfor(a, 0);//执行应用程序,等待他执行完毕
run(a); //创建pross子进程command的应用程序a->command
waitpid(runpid, &status, 0);//等待子进程结束
delete_init_action(a);//删除这个执行后的结构体
run_actions(WAIT);
waitfor(a, 0);//执行应用程序,等待他执行完毕
run(a); //创建pross子进程command的应用程序a->command
waitpid(runpid, &status, 0);//等待子进程结束
delete_init_action(a);//删除这个执行后的结构体
run_actions(ONCE);
run(a);
delete_init_action(a);
/* Now run the looping stuff for the rest of forever */
while (1) {//退出后的子进程重新运行
/* run the respawn stuff */
run_actions(RESPAWN);
if (a->pid == 0)
{
a->pid = run(a);
}
/* run the askfirst stuff */
run_actions(ASKFIRST);
if (a->pid == 0)
{
a->pid = run(a);
打印出:Please press Enter to activate this console.
等待回车:while (read(0, &c, 1) == 1 && c != '\n');
创建子进程
}
/* Don't consume all CPU time -- sleep a bit */sleep(1);
/* Wait for a child process to exit */
wpid = wait(NULL);//等待子进程退出
while (wpid > 0) {//退出后的子进程的id设置为0
/* Find out who died and clean up their corpse */
for (a = init_action_list; a; a = a->next) {
if (a->pid == wpid) {
/* Set the pid to 0 so that the process gets
* restarted by run_actions() */
a->pid = 0;
message(L_LOG, "process '%s' (pid %d) exited. "
"Scheduling it for restart.",
a->command, wpid);
}
}
/* see if anyone else is waiting to be reaped */
wpid = waitpid(-1, NULL, WNOHANG);
}
if (a->action & ASKFIRST) { //ALIGN1 对齐方式. 不是标准关键字,要看编译器
static const char press_enter[] ALIGN1 =
#ifdef CUSTOMIZED_BANNER
#include CUSTOMIZED_BANNER
#endif
"\nPlease press Enter to activate this console. ";
full_write(1, press_enter, sizeof(press_enter) - 1);
如果是askfirst则打印出:Please press Enter to activate this console.
init进程分析总结:
1.需要配置文件:
dev/console dev/null //stdin stdout定位到dev/null中
etc/inittab
配置文件里指定的应用程序
2.库(c库等)
3.init本身bosybox
最小根文件系统的组成即是:
1.dev/console dev/null
2.bosybox
3.etc/inittab
4.配置文件里指定的应用程序
5.c库