这是有关用户空间应用程序的问题,但是请听我说!
可以说,启动Linux的功能发行版需要三个“应用程序”:
Bootloader-对于嵌入式设备,通常不是U-Boot,虽然不是硬性要求。
内核-非常简单。
根文件系统-没有它就无法启动到外壳。包含内核引导至的文件系统,并在其中
init
称为表格。
我的问题是关于#3的。如果有人想构建一个极小的rootfs(对于这个问题,可以说没有GUI,仅shell),那么引导到shell需要什么文件/程序?
这是有关用户空间应用程序的问题,但是请听我说!
可以说,启动Linux的功能发行版需要三个“应用程序”:
Bootloader-对于嵌入式设备,通常不是U-Boot,虽然不是硬性要求。
内核-非常简单。
根文件系统-没有它就无法启动到外壳。包含内核引导至的文件系统,并在其中init
称为表格。
我的问题是关于#3的。如果有人想构建一个极小的rootfs(对于这个问题,可以说没有GUI,仅shell),那么引导到shell需要什么文件/程序?
Answers:
这完全取决于您想要在设备上提供哪些服务。
您可以使Linux直接引导到shell中。它在生产环境中不是很有用-只是想让外壳坐在那里-但是当您有交互式引导程序时,它作为干预机制很有用:传递init=/bin/sh
给内核命令行。所有Linux系统(和所有unix系统)在中都有Bourne / POSIX风格的shell /bin/sh
。
您将需要一组Shell实用程序。BusyBox是一个非常常见的选择。它包含文件和文本处理(外壳和共同事业cp
,grep
...),网络设置(ping
,ifconfig
,...),工艺操作(ps
,nice
,...),以及其他各种系统工具(fdisk
,mount
,syslogd
,...)。BusyBox具有极高的可配置性:您可以在编译时选择所需的工具,甚至可以选择各个功能,以获得适合您的应用程序的正确尺寸/功能。除此之外sh
,最起码,你真的不能做任何事情,而不是mount
,umount
和halt
,但它是非典型的不也有cat
,cp
,mv
,rm
,mkdir
,rmdir
,ps
,sync
和几个。BusyBox作为一个名为的二进制文件安装busybox
,每个实用程序都有一个符号链接。
在普通的UNIX系统上,第一个过程称为init
。它的工作是启动其他服务。BusyBox包含一个初始化系统。除了init
二进制文件(通常位于中/sbin
)之外,您还需要其配置文件(通常称为/etc/inittab
-一些现代的init替代品会删除该文件,但在小型嵌入式系统上找不到它们),这些文件会指示要启动的服务什么时候。对于BusyBox,/etc/inittab
是可选的;如果缺少它,您将在控制台上获得一个根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
,lib
和sbin
。/usr
存在于不在根文件系统上的其他文件。如果没有,您可以/usr
建立指向根目录的符号链接。以下是一些最低限度的典型条目/dev
:
console
full
(向其写入始终报告“设备上没有剩余空间”)log
(程序用来发送日志条目的套接字),如果您有syslogd
守护进程(例如BusyBox的)正在从中读取null
(就像一个始终为空的文件)ptmx
和pts
目录,如果您想使用伪终端(即控制台以外的任何终端),例如,如果设备已联网并且想要通过telnet或ssh登录random
(返回随机字节,有阻塞的危险)tty
(总是指定程序的终端)urandom
(返回随机字节,从不阻塞,但在新启动的设备上可能是非随机的)zero
(包含无限个空字节序列)除此之外,您还需要硬件的条目(网络接口除外,这些条目不会在中获得条目/dev
):串行端口,存储设备等。
对于嵌入式设备,通常可以直接在根文件系统上创建设备条目。高端系统具有一个MAKEDEV
用于创建/dev
条目的脚本,但是在嵌入式系统上,该脚本通常不捆绑到映像中。如果可以热插拔某些硬件(例如,如果设备具有USB主机端口),/dev
则应由udev管理(您在根文件系统上可能仍具有最小设置)。
除了根文件系统,您还需要安装一些其他文件才能正常运行:
/proc
(几乎必不可少)/sys
(几乎必不可少)tmpfs
上的文件系统/tmp
(以允许程序创建将在RAM中而不是在闪存或只读中的根文件系统上的临时文件)/dev
如果是动态的)(请参见上面“设备文件”中的udev)/dev/pts
,如果你想使用[伪终端(请参阅有关此言pts
以上)您可以制作/etc/fstab
文件并调用mount -a
,也可以mount
手动运行。
如果您有写日志的地方,请启动syslog守护程序(以及klogd
内核日志,如果syslogd
程序不处理的话)。
此后,设备准备启动特定于应用程序的服务。
这是一个漫长而多样的故事,所以我在这里要做的只是提供一些指导。
根文件系统可以保存在RAM中(从ROM或Flash中的(通常是压缩的)映像加载),或在基于磁盘的文件系统中(存储在ROM或Flash中),或者从网络(通常通过TFTP)加载。。如果根文件系统位于RAM中,则将其设置为initramfs —一种RAM文件系统,其内容在引导时创建。
存在许多用于为嵌入式系统组装根映像的框架。在BusyBox FAQ中有一些指示。Buildroot是一种流行的方法,它允许您使用类似于Linux内核和BusyBox的设置来构建整个根映像。OpenEmbedded是另一个这样的框架。
Wikipedia有一个(不完整的)流行的嵌入式Linux发行列表。您可能会遇到的嵌入式Linux的一个示例是用于网络设备的OpenWrt操作系统家族(在修补匠的家庭路由器上很受欢迎)。如果您想从经验中学习,可以尝试Scratch的Linux,但这是针对爱好者的台式机系统,而不是嵌入式设备。
植入Linux内核的唯一行为是在引导时启动的第一个程序。(我不会在这里讨论initrd和initramfs的细节。)该程序传统上称为init,具有进程ID 1,并且具有某些特权(对KILL信号具有免疫性)和职责(对孤儿有收割)。您可以运行在Linux内核的系统,并开始任何你想要作为第一个过程,但你拥有的是基于Linux内核的操作系统,而不是通常所谓的“Linux” - Linux的,在常识这个术语是类似于Unix的操作系统,其内核是Linux内核。例如,Android是一个不像Unix的操作系统,但是基于Linux内核。
您所需要的只是一个静态链接的可执行文件,该文件独立存在于文件系统中。您不需要任何其他文件。该可执行文件是初始化过程。它可以是busybox。这样就给您提供了一个Shell和许多其他实用程序,全部就在其中。您可以通过在busybox中手动执行命令以挂载读写的根文件系统,创建/ dev节点,exec real init等方式进入功能全面的系统。
如果不需要任何shell实用程序,则可以使用静态链接的mksh
二进制文件(例如,针对klibc –在Linux / i386上为130K)。您需要只在循环中调用的/linuxrc
or /init
或/sbin/init
脚本mksh -l -T!/dev/tty1
:
#!/bin/mksh
while true; do
/bin/mksh -l -T!/dev/tty1
done
该-T!$tty
选项是mksh
对它的最新添加,它告诉它在给定的终端上生成一个新的shell并等待它。(在此之前,只有-T-
到dæmonise一个PROGRAMM和-T$tty
产卵终端上而不是等待它。这不是那么好。)的-l
选项只是告诉它来运行一个登录shell(读取/etc/profile
,~/.profile
和~/.mkshrc
)。
假设您的终端为/dev/tty1
,替代。(有了更多的魔力,可以自动找到终端。/dev/console
将无法完全控制工作。)
您需要一些文件/dev
才能起作用:
使用kernel选项引导devtmpfs.mount=1
无需再填充目录/dev
,只需将其设为空目录(适合用作挂载点)即可。
通常,您将需要一些实用程序(来自klibc,busybox,beastiebox,toybox或Toolbox),但实际上并不需要它们。
您可能想要添加一个~/.mkshrc
文件,该文件设置$ PS1以及一些基本的shell别名和功能。
我曾经仅使用mksh(及其示例mkshrc文件)和klibc-utils为Linux / m68k制作了171K压缩(371K未压缩)initrd。(不过,这是在将-T!添加到shell之前的,因此它/dev/tty2
改为生成了登录shell ,并向控制台回显了一条消息,告诉用户切换终端。)它可以正常工作。
这是一个真正的最低设置。其他答案为功能更强大的系统提供了极好的建议。这是真正的特殊情况。
免责声明:我是mksh开发人员。
mksh
。
最小的init hello world程序逐步
编译一个没有任何以无限循环结尾的依赖项的hello世界。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
我们不能使用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"
这会创建一个文件系统,其问候世界位于/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"
并且您应该看到一行:
FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR
在模拟器屏幕上!请注意,这不是最后一行,因此您必须往前看一点。
如果静态链接C程序,也可以使用它们:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n");
sleep(0xFFFFFFFF);
return 0;
}
与:
gcc -static init.c -o init
您可以在带有USB的真实硬件上运行,/dev/sdX
并且:
make isoimage FDINITRD="$ROOTFS_PATH"
sudo dd if=arch/x86/boot/image.iso of=/dev/sdX
关于此主题的绝佳资源:http : //landley.net/writing/rootfs-howto.html它还说明了如何使用gen_initramfs_list.sh
,它是Linux内核源代码树中的一个脚本,用于帮助自动完成该过程。
下一步:设置BusyBox,以便您可以与系统互动:https://github.com/cirosantilli/runlinux
已在Ubuntu 16.10,QEMU 2.6.1上测试。