我正在经历一个设置自定义 initramfs 的教程其中指出:
唯一缺少的是 /init,它是 initramfs 根目录中的可执行文件,一旦加载,就会由内核执行。因为 sys-apps/busybox 包含一个功能齐全的 shell,这意味着您可以将 /init 二进制文件编写为简单的 shell 脚本(而不是使其成为必须编译的用汇编程序或 C 编写的复杂应用程序)。
并给出了 init 作为 shell 脚本的示例,其开头为#!/bin/busybox sh
到目前为止,我的印象是 init 是启动的主进程,所有其他用户空间进程最终都是 init 的子进程。然而,在给定的示例中,第一个进程实际上是bin/busybox/ sh
从中生成后来的 init 的。
这是正确的解释吗?例如,如果我当时有一个可用的解释器,我可以将 init 编写为 Python 脚本等?
答案1
init 不是“生成”(作为子进程),而是像exec
这样:
# Boot the real thing.
exec switch_root /mnt/root /sbin/init
exec
替换整个流程。最终的 init 仍然是第一个进程 (pid 1),尽管它之前是 Initramfs 中的进程。
Initramfs/init
是一个 Busybox shell 脚本,pid 为 1,exec
s 到 Busybox switch_root
(所以现在switch_root
是 pid 1);该程序会更改您的安装点,因此/mnt/root
将是新的/
。
switch_root
然后再次exec
访问/sbin/init
您真正的根文件系统;因此,它使您真正的 init 系统成为 pid 1 的第一个进程,这反过来可能会产生任意数量的子进程。
当然,如果您设法将 Python 烘焙到 Initramfs 中,那么也可以使用 Python 脚本来完成。尽管如果您无论如何都不打算包含 busybox,则您将不得不费力地重新实现它的一些功能(例如switch_root
,以及您通常使用简单命令执行的其他所有操作)。
但是,它不适用于不允许脚本二进制文件 ( CONFIG_BINFMT_SCRIPT=y
) 的内核,或者在这种情况下,您必须直接启动解释器并使其以某种方式加载您的脚本。
答案2
Linux 内核的 exec 系统调用本身就理解 shebangs
当执行的文件以 magic bytes 开头时#!
,它们告诉内核使用#!/bin/sh
:
- do 和
exec
系统调用 - 具有可执行文件
/bin/sh
- 并使用 CLI 参数:当前脚本的路径
这与运行常规用户空间 shell 脚本时发生的情况完全相同:
./myscript.sh
如果文件以 magic bytes.ELF
而不是开头#!
,内核将选择 ELF 加载程序来运行它。
更多详情请参见:为什么人们在 Python 脚本的第一行写#!/usr/bin/env python shebang? |堆栈溢出
一旦您记住了这一点,就很容易接受/init
内核可以执行的任何内容,包括 shell 脚本,以及为什么/bin/sh
在这种情况下将是第一个可执行文件。
对于那些想要尝试的人来说,这是一个最小的可运行示例:https://github.com/cirosantilli/linux-kernel-module-cheat/tree/cbea7cc02c868711109ae1a261d01fd0473eea0b#custom-init