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(一)淺析
沒有留言:
張貼留言