定制一个最小的linux 系统

一个操作系统宏观上分为两个部分,kernel和shell(核和壳),kernel就是操作系统内核,shell在kernel之上,提供与用户交互的界面,包括CLI(命令行界面)和GUI(图形用户界面)。除此之外还有基础运行库(如c库、posix)等基础软件。只有kernel,不能算作一个操作系统,因为什么也做不了。本文通过linux内核和busybox工具集,制作一个最简单的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

建议直接从https://www.kernel.org/ 下载 tar -xvJf linux-5.15.175.tar.xz

官方这个这个拉下来大概5g多

git clone https://github.com/torvalds/linux.git

#或者国内的镜像
git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git

git checkout

make ARCH=x86_64 defconfig

make menuconfig

make bzImage -j

如果卡死,可能是内存过小,可单线程编译

编译busybox

获取并编译busybox

git clone https://github.com/mirror/busybox.git

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

制作 initramfs

bin sbin  usr dev  etc  init  proc    sys  

cd busybox/_install
rm linuxrc
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

# 检查是否为 PID 1
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

# 限制文件执行、SUID 和设备文件的创建权限。
mount -t proc proc /proc
mount -t sysfs -o noexec,nosuid,nodev sysfs /sys

# 用 /sbin/init代替当前进程,将 /sbin/init 的输入、输出和错误流全部重定向到 /dev/console
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:/bin/sh
tty2::respawn:/bin/sh
ttyS0::respawn:/bin/sh

# 可选,可配置用户权限
tty1::respawn:/sbin/getty 115200 tty1
tty2::respawn:/sbin/getty 115200 tty2
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100

编辑 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
}

# 配置 mdev
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
}

# 启动 acpid 服务, 管理电源按钮、睡眠等 ACPI 相关事件。
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

可选,配置用户权限,设置用户账号密码

#!/bin/sh

# 创建 /etc/passwd 文件
echo "root:x:0:0:root:/root:/bin/sh" > etc/passwd

# 替换yourpassword为自己的密码, 使用 openssl 生成加密密码
echo "root:$(openssl passwd -6 root):0:0:99999:7:::" > etc/shadow
# sudo bash -c '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/

# 拷贝内核文件和 initramfs
sudo cp linux-5.15.175/arch/x86_64/boot/bzImage tmpimg/
sudo cp busybox/initramfs.cpio.gz tmpimg/

# 安装grub
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
}

运行

qemu-system-x86_64 -hda tiny-linux.img -nographic
-nographic 没有图形界面时候使用,可以在终端显示


# 如果希望在vmware中运行,可以转换磁盘格式,添加到虚拟磁盘
qemu-img convert -f raw -O vmdk tiny-linux.img tiny-linux.img.vmdk