我正在尝试在 Linux 系统上调试 init 脚本;我试图传递init=/bin/sh
给内核以使其sh
在不启动的情况下启动init
,这样我就可以手动运行初始化序列。
我发现内核init
无论如何都会启动。在启动过程中,printk 消息之一是命令行,这表明该行已正确设置;此外,我可以使用内核命令行影响其他事情。我已检查以确保该路径存在;确实如此。
这是一个busybox系统,init是busybox的符号链接;因此,为了确保 busybox 在 PID 为 1 时不会执行奇怪的魔法,我还尝试运行一个非 busybox 程序作为 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);
where__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
。
另请注意,ram 初始化和非 ram 初始化之间还有更多区别,例如控制台处理:嵌入式 initramfs 与外部 initramfs 执行 init 的区别?
答案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 存在,内核将始终尝试运行它,假设它正在进行 ramdisk 引导。检查您的系统,看看 /init 是否存在,如果存在,那么这可能是您的问题。
答案4
您可以自定义 Linux 内核并重新编译它。对于 4.9 内核,编辑 init/main.c 中的函数“kernel_init”并尝试首先运行以下行:
try_to_run_init_process("/bin/sh")
另外,也可能是BootLoader传递的内核参数导致的。