2020年2月9日 星期日

busybox init flow


Linux在啟動kernel後,便由kernel載入init程式,由init程式完成餘下的啟動過程,簡略片段如下
Kernel 4.14
start_kernel()
  |--> rest_init()
    |--> pid = kernel_thread(kernel_init, NULL, CLONE_FS);
      |--> kernel_init()
       if (!try_to_run_init_process("/sbin/init") ||
          !try_to_run_init_process("/etc/init") ||
          !try_to_run_init_process("/bin/init") ||
          !try_to_run_init_process("/bin/sh"))
          return 0;
       panic("No working init found.  Try passing init= option to kernel. "
            "See Linux Documentation/admin-guide/init.rst for guidance.");

接著就會交由init開始一連串的init過程,busybox的init會去讀取/etc/inittab設定來進行init,如果沒有/etc/inittab,會執行以下預設的inittab內容
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh

其格式為<id>:<runlevels>:<action>:<process>,說明如下
<id>: 與傳統init意思不同, 會把stdin/stdout設為/dev/<id>

<runlevels>: busybox會ignore該欄位

<action>: 有效的action包含sysinit, wait, once, respawn, askfirst, shutdown, restart and ctrlaltdel.
  "sysinit" 第一個被執行的action, init會等待"sysinit"執行完成後, 再執行wait與once
  "askfirst"/"respawn" 接著被執行, 但是askfirst執行前會先印"Please press Enter to activate this console". 

<process>: 要執行的命令

inittab的action執行code flow如下
# busybox v1.32

static pid_t run(const struct init_action *a)
{
  ...
  if (BB_MMU && (a->action_type & ASKFIRST)) {
    static const char press_enter[] ALIGN1 =
#ifdef CUSTOMIZED_BANNER
#include CUSTOMIZED_BANNER
#endif
        "\nPlease press Enter to activate this console. ";
    full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1);
    ...
  }
  ...
}

static void run_actions(int action_type)
{
  struct init_action *a;

  for (a = G.init_action_list; a; a = a->next) {
    if (!(a->action_type & action_type))
      continue;

    if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) {
        pid_t pid = run(a);
      if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN))
          waitfor(pid);
    }
    if (a->action_type & (RESPAWN | ASKFIRST)) {
      /* Only run stuff with pid == 0. If pid != 0,
       * it is already running
       */
      if (a->pid == 0)
        a->pid = run(a);
    }
  }
}

int init_main(int argc UNUSED_PARAM, char **argv)
{
  ...
  /* Check if we are supposed to be in single user mode */
  if (argv[1]
   && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
  ) {
    /* ??? shouldn't we set RUNLEVEL="b" here? */
    /* Start a shell on console */
    new_init_action(RESPAWN, bb_default_login_shell, "");
  } else {
    /* Not in single user mode - see what inittab says */

    /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
    * then parse_inittab() simply adds in some default
    * actions (i.e., INIT_SCRIPT and a pair
    * of "askfirst" shells) */
    parse_inittab();
  }

  ...
  /* Now run everything that needs to be run */
  /* First run the sysinit command */
  run_actions(SYSINIT);
  check_delayed_sigs(&G.zero_ts);
  /* Next run anything that wants to block */
  run_actions(WAIT);
  check_delayed_sigs(&G.zero_ts);
  /* Next run anything to be run only once */
  run_actions(ONCE);
  ...
  while (1) {
    ...
    /* (Re)run the respawn/askfirst stuff */
    run_actions(RESPAWN | ASKFIRST);
    ...
  }
  ...
}


如果要進入signle user mode,就需要傳遞"single"或"-S"給init當參數,要把字串放在kernel parameters "--" 之後
The kernel parses parameters from the kernel command line up to “–”; 
if it doesn’t recognize a parameter and it doesn’t contain a ‘.’, 
the parameter gets passed to init: parameters with ‘=’ go into init’s environment, 
others are passed as command line arguments to init.
Everything after “–” is passed as an argument to init.

如:
qemu-system-arm -M vexpress-a9 -m 512M -kernel ./linux/arch/arm/boot/zImage -dtb \
./linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd ./initrd-arm.img \
-nographic -append "console=ttyAMA0 -- single"

busybox並不支援runlevel
    參考資料:
  • https://en.wikipedia.org/wiki/Init, init
  • https://en.wikipedia.org/wiki/Runlevel, runlevel
  • https://git.busybox.net/busybox/tree/examples/inittab, busybox example for inittab
  • https://www.itread01.com/p/1350587.html, kernel 啟動流程之 【裝置驅動載入】 學習筆記
  • https://www.itread01.com/content/1543246633.html, busybox(一)淺析

沒有留言:

張貼留言

熱門文章