定制一个最小的linux 系统
tiny-linux.img
内核5.x 用户名密码 root/root
一个操作系统宏观上分为两个部分,kernel和shell(核和壳),kernel就是操作系统内核,shell在kernel之上,提供与用户交互的界面,包括CLI(命令行界面)和GUI(图形用户界面)。除此之外还有基础运行库(如c库、posix)等基础软件。只有kernel,不能算作一个操作系统,因为什么也做不了。本文通过linux内核和busybox工具集,制作一个最简单的linux系统。
背景熟悉
linux开机启动流程

环境准备
安装必要的软件包
sudo apt update
sudo apt upgrade
sudo apt install build-essential git kconfig-frontends flex bison libelf-dev bc libssl-dev qemu qemu-system-x86
开始
编译 linux-kernel
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.138.tar.gz
|
# 官方这个这个拉下来大概5g多 git clone https:
#或者国内的镜像 git clone https:
git checkout
# 建议直接从 https: wget https:
#使用x86_64架构的默认配置文件来生成内核的.config文件 make ARCH=x86_64 defconfig
# 也可使用 意思是我们编译内核的配置采用用当前环境一致的配置 cp -v /boot/config-$(uname -r) .config make defconfig
make menuconfig
make bzImage -j
如果卡死,可能是内存过小,可单线程编译
|
这里通过kernel源码可以熟悉简单的启动流程如下图

编译busybox
获取并编译busybox
git clone https:
git checkout 1_36_stable
make menuconfig
|
启用静态链接:Busybox Settings -> Build Options -> Build BusyBox as a static binary (no shared libs)。
make -j$(nproc) make install
|
这里通过busybox源码看到简单的启动流程:
init_main() (init/init.c)也即busybox中的init进程入口。init上承kernel,下起用户空间进程,配置了整个用户空间工作环境。
首先初始化串口、环境变量等;parse_inittab() 解析/etc/inittab文件;初始化信号处理函数;然后run_actions() 依次执行SYSINIT、WAIT、ONCE选项;最后在while(1)中监控RESPAWN|ASKFIRST选项。

制作 initramfs
bin sbin usr dev etc init proc sys
cd busybox/_install rm linuxrc # 在 BusyBox 引导为 init 时,它默认会尝试执行 linuxrc。我们通常希望使用自己的 init 脚本。 mkdir -p etc/init.d
# 下面这个如不复制,可以在init之前切换到用户态sysint时候创建 sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
# echo "::sysinit:/bin/sh" > etc/inittab
|
编辑 etc/init.d/init文件
#!/bin/busybox sh
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
if [ "$$" -ne 1 ]; then echo "Error: This script can only be run as the init process (PID 1)." >&2 exit 1 fi
[ -d /proc ] || mkdir -p /proc [ -d /dev ] || mkdir -p /dev [ -d /sys ] || mkdir -p /sys [ -d /tmp ] || mkdir -p /tmp [ -d /var/run ] || mkdir -p /var/run [ -d /root ] || mkdir -p /root
mount -t proc proc /proc mount -t sysfs -o noexec,nosuid,nodev sysfs /sys
exec /sbin/init < /dev/console > /dev/console 2>&1
|
编辑etc/inittab文件
::sysinit:/etc/init.d/sysinit ::ctrlaltdel:/sbin/reboot ::shutdown:/sbin/poweroff ::restart:/sbin/init
tty1::respawn:/sbin/getty tty1 tty2::respawn:/sbin/getty tty2 ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100
|
/etc/inittab中不同action类型有着先后顺序:SYSINIT > WAIT > ONCE > RESPAWN | ASKFIRST。

/etc/inittab解析
nittab文件中一行表示一个action。Format for each entry: :::
id表示process使用的tty设备;runlevels在busybox中不支持;action是sysinit、wait、once、respawn、askfirst中的一种;process是命令及其参数。
编辑 etc/init.d/sysinit
#!/bin/sh
echo "Tiny linux Starting sysinit..."
create_device_nodes() { echo "Creating essential device nodes..." mknod -m 666 /dev/null c 1 3 mknod -m 600 /dev/console c 5 1 mknod -m 666 /dev/tty c 5 0
for i in 0 1 2; do mknod -m 620 /dev/tty$i c 4 $i done
mknod -m 620 /dev/ttyS0 c 4 64 }
mount_filesystems() { echo "Mounting virtual filesystems..." mount -t devtmpfs devtmpfs /dev mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t devpts devpts /dev/pts mount -t tmpfs tmpfs /dev/shm }
setup_mdev() { echo "Setting up mdev..." mdev -s }
create_standard_links() { echo "Creating standard stream links..." ln -snf /proc/self/fd /dev/fd ln -snf fd/0 /dev/stdin ln -snf fd/1 /dev/stdout ln -snf fd/2 /dev/stderr [ -e /proc/kcore ] && ln -snf /proc/kcore /dev/core }
load_sysctl() { if [ -e /etc/sysctl.conf ]; then echo "Loading sysctl settings..." sysctl -p /etc/sysctl.conf fi }
start_acpid() { if [ -e /proc/acpi ]; then echo "Starting acpid..." acpid &>/dev/null fi }
main() { create_device_nodes mount_filesystems setup_mdev create_standard_links load_sysctl start_acpid echo "Sysinit completed." }
main
|
根目录init链接到etc/init.d/sysinit
ln -s etc/init.d/init init
|
可选,配置用户权限,设置用户账号密码
#!/bin/sh
echo "root:x:0:0:root:/root:/bin/sh" > etc/passwd
echo "root:$(openssl passwd -6 root):0:0:99999:7:::" > etc/shadow
chmod 644 etc/passwd && chmod 600 etc/shadow
|
最后的一些权限准备
sudo chmod +x etc/init.d/init sudo chmod +x etc/init.d/sysinit
cd .. sudo chown -R root:root _install/
|
最终文件目录结构如下

生成最终需要的 initramfs.cpio.gz
find . | cpio -o --format=newc | gzip > ../initramfs.cpio.gz
|
准备磁盘和grub
dd if=/dev/zero of=tiny-linux.img bs=1M count=32
sudo losetup -Pf --show tiny-linux.img fdisk /dev/loopX n p w sync
sudo mkfs.ext4 /dev/loopXp1
mkdir -p tmpimg sudo mount /dev/loopXp1 tmpimg/
sudo cp linux-5.15.175/arch/x86_64/boot/bzImage tmpimg/ sudo cp busybox/initramfs.cpio.gz tmpimg/
sudo grub-install --boot-directory=tmpimg/boot/ --target=i386-pc --modules=part_msdos tiny-linux.img
|
编辑grub2 配置 tmpimg/boot/grub/grub.cfg
set default=0 set timeout=5
menuentry 'CA Tiny Linux' { linux /bzImage console=tty0 console=ttyS0,115200 initrd /initramfs.cpio.gz }
|
卸载
sync umount tmpimg
losetup -d /dev/loopx
|
运行
qemu-system-x86_64 -hda tiny-linux.img -nographic
qemu-img convert -f raw -O vmdk tiny-linux.img tiny-linux.img.vmdk
|
参考链接
(排名不分先后)
https://www.ruanyifeng.com/blog/2013/02/booting.html
https://www.cnblogs.com/arnoldlu/p/10868354.html
https://blog.csdn.net/y3over/article/details/51382352
https://blog.51cto.com/u_5122542/906655
https://www.vxworks.net/linux/1042-linux-power-on-boot-process