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(一)淺析
