[unix] init = / path / to / program을 커널에 전달하여 프로그램을 init로 시작하지 않는 것은 무엇입니까?

Linux 시스템에서 init 스크립트를 디버깅하려고합니다. init=/bin/sh커널을 시작 sh하지 않고 시작 init하도록 초기화 하려고 하므로 초기화 시퀀스를 수동으로 실행할 수 있습니다.

내가 찾은 것은 init어쨌든 커널이 시작되고 있다는 것입니다. 부팅하는 동안 printk 메시지 중 하나가 명령 줄이며, 해당 줄이 올바르게 설정되어 있음을 나타냅니다. 또한 커널 명령 줄을 사용하여 다른 것에 영향을 줄 수 있습니다. 경로가 존재하는지 확인했습니다. 그렇습니다.

이것은 busybox 시스템이며 init는 busybox에 대한 심볼릭 링크입니다. 따라서 busybox가 PID가 1 일 때 이상한 마술을하지 않도록하기 위해 비 busybox 프로그램을 init로 실행 해 보았습니다. 그것도 작동하지 않았다. 내가 무엇을하든 초기화가 실행되는 것 같습니다.

이 문제의 원인은 무엇입니까?



답변

리눅스 커널 소스를 살펴보면, / init 파일이 존재하면 커널은 항상 램 디스크 부팅을하고 있다는 가정하에 커널을 실행하려고 시도합니다. 시스템에 / init가 있는지 확인하십시오. 존재하는 경우 문제 일 수 있습니다.


답변

initrd shenanigans

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커널 “entry point”는을 호출 하고 스레드에서 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를 사용한 초기화 실행의 차이?


답변

의 위에

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

나는 찾았다 :

일반적인 루트 파일 시스템을 디버깅 할 때는 “init = / bin / sh”로 부팅하는 것이 좋습니다. initramfs에 해당하는 “rdinit = / bin / sh”이며 유용합니다.

따라서 아마도 ridinit = / bin / sh를 시도하십시오


답변

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

try_to_run_init_process("/bin/sh")

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


답변