Linux启动流程 | kernel执行第一个init应用程序的实现原理

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种,如下图所示:

0f61a51d2858aa31c63a3738ff8e3499.png

2. 创建init进程的方式

2.1 ramdisk方式

在ramdisk环境下创建init进程时,需要在kernel CMDLINE中设置init程序的路径位置,如下所示:

CONFIG_CMDLINE="...root=/dev/ram rdinit=/sbin/init..."

在kernel代码中通过rdinit_setup()解析kernel CMDLINErdinit=字符串,赋值给全局变量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"

在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.");
}



推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值