构造根文件系统之init进程分析

来源:互联网 发布:淘宝刷流量软件排名 编辑:程序博客网 时间:2024/05/21 21:50

当我们启动了开发板后进入了文件系统,执行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 的函数实现是这样的 :

 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库

阅读全文
0 0
原创粉丝点击