linux創建init進程的3種實現方式原理分析【轉】

轉自:https://blog.csdn.net/weixin_43644245/article/details/121562388

1. 概述
Linux系統啓動過程中通過init_task創建0號idle進程。然後通過kernel_thread創建1號init進程。創建該進程時通過系統調用,在內核空間執行用戶空間的/sbin/init程序,通過該程序產生出shell,並依賴init衍生出其他進程。通過top命令查看當前系統環境下的進程列表,可以發現1號進程的爲{linuxrc} init

[root@iTOP-4412]# top
Mem: 26404K used, 948572K free, 0K shrd, 3199543672K buff, 0K cached
CPU: 0.0% usr 6.0% sys 0.0% nic 94.0% idle 0.0% io 0.0% irq 0.0% sirq
Load average: 0.00 0.00 0.00 1/78 162
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
162 132 root R 3264 0.3 0 4.5 top
3 2 root IW 0 0.0 0 1.5 [kworker/0:0]
132 1 root S 3268 0.3 2 0.0 -/bin/sh
1 0 root S 3264 0.3 2 0.0 {linuxrc} init
...

我們在kernel代碼中會發現,創建1號init進程,主要包括以下3種,如下圖所示:

 

 

 

2. 創建init進程的方式
2.1 ramdisk方式
在ramdisk環境下創建init進程時,需要在kernel CMDLINE中設置init程序的路徑位置,如下所示:

CONFIG_CMDLINE="...root=/dev/ram rdinit=/sbin/init..."
1
在kernel代碼中通過rdinit_setup()解析kernel CMDLINE中rdinit=字符串,賦值給全局變量ramdisk_execute_command。

static int __init rdinit_setup(char *str)
{
unsigned int i;

ramdisk_execute_command = str;
/* See "auto" comment in init_setup */
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("rdinit=", rdinit_setup);

當完成ramdisk_execute_command賦值後,在kernel_init_freeable()對ramdisk_execute_command進行檢查,若未檢查到有效的字符串,則將ramdisk_execute_command賦值爲/init。然後,對ramdisk_execute_command進行訪問權限檢查,若失敗,則進行rootfs掛載。

static noinline void __init kernel_init_freeable(void)
{
...
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";

if (ksys_access((const char __user *)
ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
...
}

若ramdisk_execute_command檢查成功,則進入kernel_init()中,執行指定的init程序。

static int __ref kernel_init(void *unused)
{
int ret;

kernel_init_freeable();
...
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
return 0;
pr_err("Failed to execute %s (error %d)\n",
ramdisk_execute_command, ret);
}
...
}

2.2 execute_command方式
通過kernel CMDLINE可以設定執行的init程序,例如:

CONFIG_CMDLINE="root=/dev/mmcblk1p2 rw console=ttySAC2,115200 init=/linuxrc rootwait"
1
在kernel代碼中通過init_setup()解析命令行參數"init=",並賦值給execute_command。

static int __init init_setup(char *str)
{
unsigned int i;

execute_command = str;
...
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);

最後,在kernel_init()中執行execute_command所指定的init程序。


static int __ref kernel_init(void *unused)
{
...
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
...
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/admin-guide/init.rst for guidance.");
}

2.3 默認方式
若以上兩種指定init程序的方式均以失敗告終,那麼內核代碼kernel_init()會執行如下4個默認的init程序,若也失敗,則內核上報panic。


static int __ref kernel_init(void *unused)
{
...
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.");
}
————————————————
版權聲明:本文爲CSDN博主「Linux與SoC」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_43644245/article/details/121562388

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章