init와 같은 프로그램을 시작하는 대신 init=/path/to/program이 커널에 전달되는 원인은 무엇입니까?

init와 같은 프로그램을 시작하는 대신 init=/path/to/program이 커널에 전달되는 원인은 무엇입니까?

저는 Linux 시스템에서 초기화 스크립트를 디버깅하려고 합니다. 초기화 시퀀스를 수동으로 실행할 수 있도록 부팅하지 않고 부팅 init=/bin/sh하도록 커널에 전달하려고 합니다 .shinit

어쨌든 커널이 init부팅된다는 것을 알았습니다. 부팅하는 동안 printk 메시지 중 하나는 명령줄이며, 이는 해당 줄이 올바르게 설정되었음을 나타내며 커널 명령줄을 사용하여 다른 사항에도 영향을 줄 수 있습니다. 경로가 존재하는지 확인했습니다.

이것은 비지박스 시스템이고 init는 비지박스에 대한 심볼릭 링크이므로 PID가 1일 때 비지박스가 이상한 일을 하지 않도록 하기 위해 비지박스가 아닌 프로그램도 init로 실행해 보았습니다. 내가 무엇을 하든 init이 실행되는 것 같습니다.

이 동작의 원인은 무엇입니까?

답변1

초기 장난

initrd 또는 initramfs를 사용하는 경우 다음 사항에 유의하세요.

  • rdinit=대신에 사용init=

  • 지정되지 않은 경우 rdinit=시도된 기본 경로는 /sbin/init, 및 입니다 /etc/init./bin/init/bin/sh/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);

__setup명령줄 인수를 처리하는 마법의 방법은 어디에 있습니까?

start_kernel, 커널 "진입점"은 스레드에서 "호출"합니다 rest_init.kernel_init

pid = kernel_thread(kernel_init, NULL, CLONE_FS);

따라서 다음 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.");
}

그리고 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();
    }

TODO: 알겠습니다 sys_access.

또한 콘솔 처리와 같이 램 초기화와 비램 초기화 사이에는 더 많은 차이점이 있습니다.초기화 실행을 위한 내장형 initramfs와 외부 initramfs의 차이점은 무엇입니까?

답변2

존재하다

https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt

내가 찾은 것 :

일반 루트 파일 시스템을 디버깅할 때 "init=/bin/sh"로 시작할 수 있으면 좋습니다. initramfs와 동등한 것은 "rdinit=/bin/sh"이며 이는 똑같이 유용합니다.

그러니ridinit=/bin/sh를 시도해 보세요.

답변3

Linux 커널 소스 코드를 살펴보면 /init 파일이 존재하면 커널이 램디스크 부팅을 수행한다고 가정하고 항상 해당 파일을 실행하려고 시도한다는 것을 발견했습니다. 시스템에 /init가 있는지 확인하세요. 만약 그렇다면 이것이 문제일 수 있습니다.

답변4

Linux 커널을 사용자 정의하고 다시 컴파일할 수 있습니다. 4.9 커널의 경우 init/main.c에서 "kernel_init" 함수를 편집하고 먼저 다음 줄을 실행해 보세요.

try_to_run_init_process("/bin/sh")

또한 BootLoader가 전달한 커널 매개변수로 인해 발생할 수도 있습니다.

관련 정보