0%

基于64位Linux系统构建一个可安装在软盘上的32位Linux系统

原始链接

https://bits.p1x.in/floppinux-an-embedded-linux-on-a-single-floppy/

https://bits.p1x.in/how-to-build-32-bit-floppinux-on-a-64-bit-os/

真心佩服这作者… …

获取Linux Kernel源码

创建工作目录

1
2
3
mkdir ~/kernel32
BASE=~/kernel32
cd BASE

从Linux Kernel的Git仓库克隆

1
git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

速度原因,直接从国内的源下载的指定版本

1
wget https://mirrors.163.com/kernel/v5.x/linux-5.13.4.tar.xz

解压

1
tar xvJf linux-5.13.4.tar.xz

更新内核配置并编译

切换目录

1
cd linux-5.13.4

生成系统默认的最小内核配置
tinyconfig - Configure the tiniest possible kernel

1
make ARCH=x86 tinyconfig

在最小配置基础之上添加必要配置

1
make ARCH=x86 menuconfig
1
Processor type and features ---> Processor family(Pentium-Pro)---> 486DX
1
Device Drivers ---> Character devices ---> Enable TTY
1
2
General setup ---> Configure standard kernel features(expert users)---> Enable support for printk
---> Initial RAM filesystem and RAM disk(initramfs/initrd)support --->只保留compressed using XZ即可
1
2
Executable file formats ---> Kernel support for ELF binaries
---> Kernel support for scripts starting with #!

编译

1
make ARCH=x86 bzImage -j`nproc`

检查并move到工作目录

1
2
file arch/x86/boot/bzImage
mv arch/x86/boot/bzImage ../

Musl

切换目录

1
cd BASE

下载并解压

1
2
wget https://musl.cc/i486-linux-musl-cross.tgz --user-agent="Mozilla/5.0 (X11;U;Linux i686;en-US;rv:1.9.0.3) Geco/2008092416 Firefox/3.0.3"
tar xvf i486-linux-musl-cross.tgz

Busybox

下载并解压

1
2
wget https://busybox.net/downloads/busybox-1.33.1.tar.bz2
tar xjvf busybox-1.33.1.tar.bz2

切换目录

1
cd busybox-1.33.1

生成配置

1
2
make ARCH=x86 allnoconfig
make ARCH=x86 menuconfig

更新配置

1
2
Settings ---> Build static binaryno shared libs)
---> Support files > 2 GB
1
Coreutils ---> cat, du, echo, ls, sleep, uname等
1
Console Utilities ---> clear
1
Editors ---> vi
1
Init Utilities ---> poweroff, reboot, init, Support reading an inittab file(NEW)
1
Linux System Utilities ---> mount, umount
1
Miscellaneous Utilities ---> less
1
Shells ---> ash, Optimize for size instead of speed, Alias support, help builtin

BASE环境变量按需配置

1
2
3
4
sed -i "s|.*CONFIG_CROSS_COMPILER_PREFIX.*|CONFIG_CROSS_COMPILER_PREFIX="\"${BASE}"i486-linux-musl-cross/bin/i486-linux-musl-\"|" .config
sed -i "s|.*CONFIG_SYSROOT.*|CONFIG_SYSROOT=\""${BASE}"i486-linux-musl-cross\"|" .config
sed -i "s|.*CONFIG_EXTRA_CFLAGS.*|CONFIG_EXTRA_CFLAGS=-I$BASE/i486-linux-musl-cross/include|" .config
sed -i "s|.*CONFIG_EXTRA_LDFLAGS.*|CONFIG_EXTRA_LDFLAGS=-L$BASE/i486-linux-musl-cross/lib|" .config

编译Busybox

1
2
make ARCH=x86 -j`nproc`
make ARCH=x86 install

创建文件系统

1
2
3
4
5
6
7
mkdir $BASE/filesystem
mv _install/* $BASE/filesystem

cd $BASE/filesystem
mkdir -pv {dev,proc,etc/init.d,sys,tmp}
sudo mknod dev/console c 5 1
sudo mknod dev/null c 1 3
1
2
3
cat >> welcome << EOF
welcome mini-linux
EOF
1
2
3
4
5
6
7
cat >> etc/inittab << EOF
::sysinit:/etc/init.d/rc
::askfirst:/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF
1
2
3
4
5
6
7
8
cat >> etc/init.d/rc << EOF
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
clear
cat welcome
/bin/sh
EOF
1
2
chmod +x etc/init.d/rc
sudo chown -R root:root .
1
find . | cpio -H newc -o | xz --check=crc32 > ../rootfs.cpio.xz

启动测试

1
2
qemu-system-i386 -kernel bzImage -initrd rootfs.cpio.xz
qemu-system-i386 -kernel bzImage -initrd rootfs.cpio.xz -vnc 192.168.0.20:0,to=99,id=default

软盘镜像(条件受限,未测试)

制作软盘镜像

1
2
3
dd if=/dev/zero of=floppinux.img bs=1k count=1440
mkdosfs floppinux.img
syslinux --install floppinux.img

挂载镜像文件并把相关文件拷贝

1
2
3
4
5
sudo mount -o loop floppinux.img /mnt
sudo cp bzImage /mnt
sudo cp rootfs.cpio.gz /mnt
sudo cp syslinux.cfg /mnt
sudo umount /mnt

可能的疑惑点

按教程测试完毕后,部分人可能会有一系列疑惑

我的Linux环境怎么没有/etc/inittab

解决此疑惑就不得不提init

init是initialization的缩写,是类Unix系统中用来产生其它进程的程序。它以守护进程的方式存在,pid为1。

Linux系统加载Linux内核后,便由内核加载init程序,由init程序完成余下的引导过程,比如:加载运行级别、加载服务、引导Shell/图形化界面等。

常见的init程序

  • SysV
  • Upstart,Ubuntu发起(Ubuntu现在也投向Systemd怀抱了)
  • Systemd,完全替代init,可并行启动服务,向下兼容System V。目前已被大多数Linux发行版采用,这可能就是你环境没有此文件的原因。
  • OpenRC,Gentoo定制
  • runit
  • … …

/etc/inittab是SysV init(或兼容)的进程调用的文件,init进程会基于此文件中的内容创建一系列进程,其每条记录(配置)的格式为

1
id:runlevels:action:process

其中有两个关键的action需了解
initdefault,下面为示例配置。其声明了系统默认启动级别,系统启动时如果找不到此配置,需要交互方式在终端手工输入

1
2
# Default runlevel.
id:3:initdefault:

sysinit,如下本文使用到的配置。sysinit中声明的程序,会在系统启动时候执行,并且优于boot和bootwait的声明

1
::sysinit:/etc/init.d/rc

注意:sysinit、boot、bootwait的声明都会执行,跟启动级别无关,其余的action都与其声明的运行级别对应

更多搜索在线的man inittab

怎么就会读取/etc/inittab

向上翻一下Busybox编译时的选项

1
Init Utilities ---> poweroff, reboot, init, Support reading an inittab file(NEW)

内核编译时的选项

1
2
General setup ---> Configure standard kernel features(expert users)---> Enable support for printk
---> Initial RAM filesystem and RAM disk(initramfs/initrd)support --->只保留compressed using XZ即可

这时基本流程清晰了: Kernel —> Init —> /bin/sh(Busybox)

再深究,就需要扣源码了… …

也就明白Kernel就是个Kernel,没有周边设施的话干不了活

Kernel咋就知道我选取的哪种init呢

内核源码init/main.c中的static int __ref kernel_init(void *unused)函数中看到如下关键信息

1
2
3
4
5
6
7
8
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.");

可以看到,源码中写死了几个路径,细心的话你会发现大多数/sbin/init文件是个软链接,链向了真实的init可执行程序

结论就是:内核不会关心你使用何种init程序的,放到如上几个路径之一即可