저는 Linux 시스템에서 초기화 스크립트를 디버깅하려고 합니다. 초기화 시퀀스를 수동으로 실행할 수 있도록 부팅하지 않고 부팅 init=/bin/sh
하도록 커널에 전달하려고 합니다 .sh
init
어쨌든 커널이 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가 전달한 커널 매개변수로 인해 발생할 수도 있습니다.