完全启动 linux 所需的最小根文件系统应用程序是什么?

MDM*_*313 17 linux startup embedded architecture root-filesystem

这是一个关于用户空间应用程序的问题,但听我说!

可以这么说,启动 Linux 的功能性发行版需要三个“应用程序”:

  1. Bootloader - 对于嵌入式,通常是 U-Boot,虽然不是硬性要求。

  2. 内核 - 这很简单。

  3. 根文件系统 - 没有它就无法引导到 shell。包含内核引导到的文件系统,其中init称为 form。

我的问题是关于#3。如果有人想构建一个极小的 rootfs(对于这个问题,我们假设没有 GUI,只有 shell),启动到 shell 需要哪些文件/程序?

Gil*_*il' 38

这完全取决于您希望在您的设备上拥有哪些服务。

程式

您可以让 Linux 直接引导至shell。它在生产中不是很有用——谁只想在那里有一个 shell——但是当你有一个交互式引导加载程序时,它作为一种干预机制很有用:传递init=/bin/sh到内核​​命令行。所有 Linux 系统(和所有 unix 系统)在/bin/sh.

您将需要一组shell 实用程序BusyBox是一个非常常见的选择;它包含一个 shell 和用于文件和文本操作 ( cp, grep, ...)、网络设置 ( ping, ifconfig, ...)、进程操作 ( ps, nice, ...) 和各种其他系统工具 ( fdisk, mount, syslogd, ...) 的通用实用程序。BusyBox 是极其可配置的:您可以在编译时选择您想要的工具,甚至是个别功能,以获得适合您的应用程序的大小/功能折衷。除此之外sh,最起码,你真的不能做任何事情,而不是mountumounthalt,但它是非典型的不也有catcpmvrmmkdir, rmdir, ps,sync等等。BusyBox 安装为名为 的单个二进制文件busybox,每个实用程序都有一个符号链接。

普通 unix 系统上的第一个进程称为init. 它的工作是启动其他服务。BusyBox 包含一个初始化系统。除了init二进制文件(通常位于/sbin),你需要它的配置文件(通常称为/etc/inittab-一些现代的init替代废除该文件,但你不会找到他们一个小型的嵌入式系统上),它们指出哪些服务启动什么时候。对于 BusyBox,/etc/inittab是可选的;如果它丢失,您会在控制台上获得一个 root shell,并且脚本/etc/init.d/rcS(默认位置)在启动时执行。

这就是您所需要的一切,当然,除了让您的设备发挥作用的程序之外。例如,在我运行OpenWrt变体的家用路由器上,唯一的程序是 BusyBox nvram(用于读取和更改 NVRAM 中的设置)和网络实用程序。

除非您的所有可执行文件都是静态链接的,否则您将需要动态加载器(ld.so,根据 libc 的选择和处理器体系结构可能以不同的名称调用)和所有动态库/lib/lib*.so,可能是其中的一些/usr/lib)所需的这些可执行文件。

目录结构

文件系统层次标准描述了Linux系统的公共目录结构。它面向桌面和服务器安装:在嵌入式系统上可以省略很多。这是典型的最小值。

  • /bin: 可执行程序(有些可能在其中/usr/bin)。
  • /dev:设备节点(见下文)
  • /etc: 配置文件
  • /lib: 共享库,包括动态加载器(除非所有可执行文件都是静态链接的)
  • /proc: proc 文件系统的挂载点
  • /sbin: 可执行程序。有区别/bin的是,/sbin对于那些只对系统管理员有用的程序,但这种区别并不在嵌入式设备上有意义的。您可以创建/sbin一个符号链接到/bin.
  • /mnt: 在维护期间将只读根文件系统作为临时挂载点很方便
  • /sys: sysfs 文件系统的挂载点
  • /tmp: 临时文件的位置(通常是tmpfs挂载)
  • /usr: 包含子目录bin,libsbin. /usr存在于不在根文件系统上的额外文件。如果你没有那个,你可以建立/usr一个到根目录的符号链接。

设备文件

以下是最小的一些典型条目/dev

  • console
  • full (写入它总是报告“设备上没有剩余空间”)
  • log(程序用来发送日志条目的套接字),如果您有一个syslogd守护进程(例如 BusyBox 的)从中读取
  • null (就像一个总是空的文件)
  • ptmx和一个pts目录,如果您想使用伪终端(即控制台以外的任何终端)——例如,如果设备已联网并且您想通过 telnet 或 ssh 登录
  • random (返回随机字节,有阻塞的风险)
  • tty (总是指定程序的终端)
  • urandom (返回随机字节,从不阻塞,但在新启动的设备上可能是非随机的)
  • zero (包含无限的空字节序列)

除此之外,您还需要硬件条目(网络接口除外,这些条目不会在 中获取条目/dev):串行端口、存储等。

对于嵌入式设备,您通常会直接在根文件系统上创建设备条目。高端系统有一个被调用MAKEDEV来创建/dev条目的脚本,但在嵌入式系统上,该脚本通常不捆绑到图像中。如果某些硬件可以热插拔(例如,如果设备具有 USB 主机端口),那么/dev应该由udev管理(您可能仍然在根文件系统上有一个最小设置)。

启动时操作

除了根文件系统,您还需要挂载一些才能正常运行:

  • procfs on /proc(几乎不可或缺)
  • sysfs on /sys(几乎不可或缺)
  • tmpfs文件系统打开/tmp(允许程序创建将在 RAM 中的临时文件,而不是在可能在闪存中或只读的根文件系统上)
  • tmpfs、devfs 或 devtmpfs on /devif dynamic(请参阅上面“设备文件”中的 udev)
  • devpts on/dev/pts如果你想使用 [pseudo-terminals (见pts上面的评论)

您可以创建一个/etc/fstab文件并调用mount -a,或者mount手动运行。

启动一个系统日志守护进程(以及klogd内核日志,如果syslogd程序不处理它),如果你有任何地方可以写入日志。

在此之后,设备准备启动特定于应用程序的服务。

如何制作根文件系统

这是一个漫长而多样的故事,所以我在这里要做的就是提供一些提示。

根文件系统可以保存在 RAM 中(从 ROM 或闪存中的(通常是压缩的)映像加载),或基于磁盘的文件系统(存储在 ROM 或闪存中),或者从网络加载(通常通过TFTP)(如果适用) . 如果根文件系统在 RAM 中,请将其设为 initramfs — 其内容在启动时创建的 RAM 文件系统。

有许多框架可用于为嵌入式系统组装根映像。BusyBox FAQ中有一些提示。Buildroot是一种流行的方法,它允许您使用类似于 Linux 内核和 BusyBox 的设置来构建整个根映像。OpenEmbedded是另一个这样的框架。

维基百科有一个(不完整的)流行的嵌入式 Linux 发行版列表。您可能拥有的嵌入式 Linux 的一个示例是用于网络设备的OpenWrt操作系统系列(在修补匠的家用路由器上很流行)。如果您想通过经验学习,您可以从零开始尝试Linux,但它面向业余爱好者的桌面系统而不是嵌入式设备。

关于 Linux 与 Linux 内核的说明

融入 Linux 内核的唯一行为是在启动时启动的第一个程序。(我不会在这里讨论initrdinitramfs 的微妙之处。)这个程序,传统上称为init,具有进程 ID 1 并具有某些特权(对KILL 信号的免疫力)和责任(收割孤儿)。你可以运行一个带有 Linux 内核的系统,然后启动任何你想要的进程作为第一个进程,但是你拥有的是一个基于 Linux 内核的操作系统,而不是通常所说的“Linux”——  Linux,在常识中是一个类 Unix操作系统,其内核是Linux 内核. 例如,Android 是一个不是类 Unix 而是基于 Linux 内核的操作系统。


Kub*_*ica 6

您只需要一个静态链接的可执行文件,单独放置在文件系统上。您不需要任何其他文件。该可执行文件是 init 进程。它可以是busybox。这为您提供了一个 shell 和许多其他实用程序,所有这些都在其本身中。您可以通过在 busybox 中手动执行命令来安装根文件系统读写、创建 /dev 节点、exec real init 等,从而进入一个功能齐全的系统。


mir*_*los 5

如果您不需要任何 shell 实用程序,则mksh可以使用静态链接的二进制文件(例如,在 Linux/i386 上针对 klibc \xe2\x80\x93 130K)。您需要一个仅在循环中调用的/linuxrcor/init或脚本:/sbin/initmksh -l -T!/dev/tty1

\n\n
#!/bin/mksh\nwhile true; do\n    /bin/mksh -l -T!/dev/tty1\ndone\n
Run Code Online (Sandbox Code Playgroud)\n\n

-T!$tty选项是最近添加的,mksh它告诉它在给定的终端上生成一个新的 shell 并等待它。(在此之前,只能-T-d\xc3\xa6monise 一个程序并-T$tty在终端上生成,但不能等待它。这不太好。)该-l选项只是告诉它运行一个登录 shell(其内容为/etc/profile,~/.profile~/.mkshrc) 。

\n\n

这假设您的终端是/dev/tty1,替换。(更神奇的是,终端可以自动被发现。/dev/console不会给你完全的作业控制权。)

\n\n

您需要一些文件/dev才能使其工作:

\n\n
    \n
  • /开发/控制台
  • \n
  • /dev/空
  • \n
  • /dev/tty
  • \n
  • /dev/tty1
  • \n
\n\n

使用内核选项引导devtmpfs.mount=1无需填充/dev,只需将其设置为空目录(适合用作挂载点)。

\n\n

您通常需要一些实用程序(来自 klibc、busybox、beastiebox、toybox 或 toolbox),但实际上并不需要它们。

\n\n

您可能想要添加一个~/.mkshrc文件,该文件设置 $PS1 和一些基本的 shell 别名和函数。

\n\n

我曾经仅使用 mksh(及其示例 mkshrc 文件)和 klibc-utils 为 Linux/m68k 制作了 171K 压缩(371K 未压缩)initrd。(不过,这是在 -T! 添加到 shell 之前,因此它生成了登录 shell,/dev/tty2并向控制台回显一条消息,告诉用户切换终端。)它工作正常。

\n\n

确实是一个最低限度的设置。其他答案为更具特色的系统提供了极好的建议。这确实是一个特例。

\n\n

免责声明:我是 mksh 开发人员。

\n


Cir*_*郝海东 5

最小初始化 hello world 程序一步一步

如这个答案所示,您所需要的只是一个静态链接的 ELF 文件,甚至不需要标准库,因此是一个具有单个文件的文件系统。

在此输入图像描述

编译一个没有任何依赖项的 hello world,并以无限循环结束。init.S:

.global _start
_start:
    mov $1, %rax
    mov $1, %rdi
    mov $message, %rsi
    mov $message_len, %rdx
    syscall
    jmp .
    message: .ascii "FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n"
    .equ message_len, . - message
Run Code Online (Sandbox Code Playgroud)

我们不能使用sys_exit,否则内核会出现恐慌。

然后:

mkdir d
as --64 -o init.o init.S
ld -o init d/init.o
cd d
find . | cpio -o -H newc | gzip > ../rootfs.cpio.gz
ROOTFS_PATH="$(pwd)/../rootfs.cpio.gz"
Run Code Online (Sandbox Code Playgroud)

这将创建一个带有 hello world 的文件系统/init,这是内核将运行的第一个用户态程序。我们还可以添加更多文件,并且当内核运行时d/可以从程序访问它们。/init

然后cd进入Linux内核树,像往常一样构建,并在QEMU中运行它:

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
git checkout v4.9
make mrproper
make defconfig
make -j"$(nproc)"
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd "$ROOTFS_PATH"
Run Code Online (Sandbox Code Playgroud)

你应该看到一行:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR
Run Code Online (Sandbox Code Playgroud)

在模拟器屏幕上!请注意,这不是最后一行,因此您必须进一步查看。

如果静态链接 C 程序,您也可以使用它们:

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n");
    sleep(0xFFFFFFFF);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

和:

gcc -static init.c -o init
Run Code Online (Sandbox Code Playgroud)

您可以在具有 USB 的真实硬件上运行,并且/dev/sdX

make isoimage FDINITRD="$ROOTFS_PATH"
sudo dd if=arch/x86/boot/image.iso of=/dev/sdX
Run Code Online (Sandbox Code Playgroud)

关于这个主题的重要资料:http://landley.net/writing/rootfs-howto.html它还解释了如何使用gen_initramfs_list.sh,这是来自 Linux 内核源代码树的脚本,可帮助自动化该过程。

为您提供 shell 的最小设置

Buildroot 是我最喜欢的选项,请参阅以下讨论:What is the Small possible Linux implementation?

此时,您基本上必须处理标准库,谁会在sh没有标准库的情况下编写 shell?因此,您最好只使用一些自动化脚本来设置所有这些。

在 Ubuntu 16.10、QEMU 2.6.1 上测试。