以linux4.19内核linux系统中第一个进程。
执行shell指令 ps -ef
结果如下:
xxx@xxx-virtual-machine:~$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 20:55 ? 00:00:02 /sbin/init splash
root 2 0 0 20:55 ? 00:00:00 [kthreadd]
root 3 2 0 20:55 ? 00:00:00 [rcu_gp]
root 4 2 0 20:55 ? 00:00:00 [rcu_par_gp]
root 5 2 0 20:55 ? 00:00:00 [slub_flushwq]
root 6 2 0 20:55 ? 00:00:00 [netns]
root 8 2 0 20:55 ? 00:00:00 [kworker/0:0H-events_highpri]
root 10 2 0 20:55 ? 00:00:00 [mm_percpu_wq]
root 11 2 0 20:55 ? 00:00:00 [rcu_tasks_rude_]
root 12 2 0 20:55 ? 00:00:00 [rcu_tasks_trace]
root 13 2 0 20:55 ? 00:00:00 [ksoftirqd/0]
root 14 2 0 20:55 ? 00:00:00 [rcu_sched]
root 15 2 0 20:55 ? 00:00:00 [migration/0]
root 16 2 0 20:55 ? 00:00:00 [idle_inject/0]
root 17 2 0 20:55 ? 00:00:00 [kworker/0:1-events]
root 18 2 0 20:55 ? 00:00:00 [cpuhp/0]
root 19 2 0 20:55 ? 00:00:00 [cpuhp/1]
root 20 2 0 20:55 ? 00:00:00 [idle_inject/1]
root 21 2 0 20:55 ? 00:00:00 [migration/1]
...
可以看到第一个进程PID为1,拉起第一个进程的指令为/sbin/init splash
内核启动流程:
start_kernel (init/main.c)
kernel_init
kernel_init函数如下:
static int __ref kernel_init(void *unused)
{
...
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);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
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.");
}
上面代码片中的两个变量:ramdisk_execute_command 和 execute_command。
其定义如下:
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
/*
* In case LILO is going to boot us with default command line,
* it prepends "auto" before the whole cmdline which makes
* the shell think it should execute a script with such name.
* So we ignore all arguments entered _before_ init=... [MJ]
*/
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);
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);
使用__setup("init=", init_setup)宏注册了这个函数,这样当内核启动时遇到
init=<command>这样的启动参数时,就会调用init_setup函数进行处理。
-
ramdisk_execute_command
ramdisk_execute_command 变量的值可以通过内核启动参数 rdinit 来设置。在Linux内核引导过程中,如果用户在命令行参数或GRUB等 bootloader配置中指定了类似 rdinit=/path/to/executable 的参数,内核在初始化initrd之后会尝试执行位于指定路径的可执行文件作为初始化脚本或进程。 -
execute_command
类似上面