什么可以使将 init=/path/to/program 传递给内核而不是将程序作为 init 启动?

Sha*_*off 14 linux init

我正在尝试在 Linux 系统上调试 init 脚本;我正在尝试传递init=/bin/sh给内核以使其在sh不启动的情况下启动,init以便我可以手动运行 init 序列。

我发现内核init无论如何都在启动。在启动期间,printk 消息之一是命令行,这表明该行设置正确;此外,我可以使用内核命令行影响其他事情。我已经检查以确保路径存在;确实如此。

这是一个busybox系统,init是busybox的符号链接;所以为了确保busybox在PID为1时不会做奇怪的事情,我还尝试将非busybox程序作为init运行;那也没有用。似乎无论我做什么,init 都会运行。

什么可能导致这种行为?

Cir*_*郝海东 13

初始恶作剧

如果您使用的是 initrd 或 initramfs,请记住以下几点:

  • rdinit= 被用来代替 init=

  • 如果rdinit=未给出,则尝试的默认路径为:/sbin/init, /etc/init, /bin/initand /bin/shbut not/init

    不使用 initrd 时,/init是尝试第一个路径,然后是其他路径。

v4.15 RTFS:所有内容都包含在https://github.com/torvalds/linux/blob/v4.15/init/main.c文件中。

首先我们了解到:

  • execute_comand 是什么传递给: init=
  • ramdisk_execute_command 是什么传递给: rdinit=

可以看出:

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);
Run Code Online (Sandbox Code Playgroud)

哪里__setup是处理命令行参数的神奇方法。

start_kernel,内核“入口点”调用rest_init,它kernel_init在线程上“调用” :

pid = kernel_thread(kernel_init, NULL, CLONE_FS);
Run Code Online (Sandbox Code Playgroud)

然后,kernel_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);
    }

    [...]

    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.");
}
Run Code Online (Sandbox Code Playgroud)

并且kernel_init_freeable

static noinline void __init kernel_init_freeable(void)
{

    [...]

    if (!ramdisk_execute_command)
        ramdisk_execute_command = "/init";

    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }
Run Code Online (Sandbox Code Playgroud)

TODO:明白sys_access

另请注意,ram inits 和非 ram inits 之间还有进一步的区别,例如控制台处理:使用嵌入式 initramfs 执行 init 与外部 initramfs 的区别?


Kyl*_*nes 3

查看 Linux 内核源代码,我发现如果文件 /init 存在,内核将始终尝试运行它,假设它正在进行 ramdisk 引导。检查您的系统,看看 /init 是否存在,如果存在,那么这可能是您的问题。