定制一个最小的linux 系统
定制一个最小的linux 系统
内核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://github.com/torvalds/linux.git
#或者国内的镜像
git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git
git checkout
# 建议直接从 https://www.kernel.org/ 下载 `tar -xvJf linux-6.15.7.tar.xz`
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-6.15.7.tar.gz
#使用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://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
这里通过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
# 检查是否为 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 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
}
# 配置 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
根目录init链接到etc/init.d/sysinit
ln -s etc/init.d/init init
可选,配置用户权限,设置用户账号密码
#!/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
}
卸载
# 安全卸载,使用sync命令确保所有缓存的写入操作都已被写入磁盘
sync
umount tmpimg
# 删除之前创建的循环设备
losetup -d /dev/loopx
运行
# -nographic 没有图形界面时候使用,可以在终端显示
qemu-system-x86_64 -hda tiny-linux.img -nographic
# 如果希望在vmware中运行,可以转换磁盘格式,添加到虚拟磁盘
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




浙公网安备 33010602011771号