2026-10-14 09:51:02 +08:00
2026-09-23 08:09:28 +00:00
2026-10-14 09:51:02 +08:00

DreamLife Linux 内核 + BusyBox 最小系统构建指南

Linux 6.12 + BusyBox + QEMU 的完整最小根文件系统。 本文档对应 build.sh 一键构建流程,包含配置说明、打包规范与故障排查。


目录结构

/home/zmn/
├── linux-6.12/               # 内核源码(./build.sh kernel-up 自动滚动到最新 6.12.x)
├── busybox/                  # BusyBox 源码
├── rootfs/                   # 根文件系统(打包源)
│   ├── init                  # 自定义 PID 1(挂载 + 首次建号 + 登录循环)
│   ├── bin/busybox           # 静态链接 BusyBox
│   ├── bin/fastfetch         # musl 动态 fastfetch
│   ├── etc/                  # passwd/group/shadow/hostname/profile/.../os-release
│   ├── home/                 # 普通用户 home(首次 setup 创建)
│   └── lib/modules/          # 内核 .ko 模块(modules_install 产物)
│   └── lib/ld-musl-x86_64.so.1  # musl 加载器(fastfetch)
├── kernel-minimal.fragment   # 内核配置增量片段
├── busybox-minimal.fragment  # BusyBox 裁剪片段
├── build.sh                  # 一键构建脚本
├── disk.img                  # 持久盘(virtio-blk ext4,build.sh 生成)
└── initramfs.cpio.gz         # 打包产物

环境准备

sudo apt update
sudo apt install -y build-essential libncurses-dev bison flex \
  libssl-dev libelf-dev git bc rsync cpio gzip wget gcc-14 qemu-system-x86

若无 gcc-14,build.sh 会自动回退到系统 gcc。 可用 CC=gcc ./build.sh 显式指定编译器。


一键构建(推荐)

cd /home/zmn
chmod +x build.sh
./build.sh            # 全量:kernel + busybox + rootfs + initramfs

分步执行:

./build.sh kernel     # 内核配置 + 编译 + 打包 initramfs(当前源码)
./build.sh kernel-up  # 内核滚动更新:拉最新点版本 + 重编 + 刷模块 + 打包(KERNEL_SERIES=7.2 换大版本)
./build.sh busybox    # BusyBox 配置 + 编译 + 安装 rootfs + 模块 + 打包
./build.sh rootfs     # 仅重装 rootfs/模块并打包
./build.sh mylogin    # 仅编译 mylogin(密码显示 *)并打包
./build.sh fastfetch  # 仅安装 fastfetch(musl 链接)并打包
./build.sh apk        # 仅安装包管理器(dpkl/apt 风格 + apk 兼容)+ 密钥/仓库/DHCP 脚本并打包
./build.sh initramfs  # 仅重新打包
./build.sh disk       # 仅生成/检查 disk.img(64MB ext4)

环境变量:

变量 默认值 说明
CC gcc-14(缺失则 gcc) C 编译器
JOBS $(nproc) 并行度
KERNEL_DIR /home/zmn/linux-6.12 内核源码(目录名固定,即使 KERNEL_SERIES 换系列也不改名,QEMU/测试均引用此路径)
KERNEL_SERIES 6.12 kernel-up 跟随的滚动系列,自动路由镜像 v6.x/v7.x 目录;换系列示例:KERNEL_SERIES=7.2 ./build.sh kernel-up(TUNA 实测最新 7.2.8)
KERNEL_CACHE /home/zmn/.cache/kernel 内核 tarball / 旧 config 备份缓存
BB_DIR /home/zmn/busybox BusyBox 源码
ROOTFS /home/zmn/rootfs 根文件系统
INITRAMFS /home/zmn/initramfs.cpio.gz 打包输出
DISK /home/zmn/disk.img 持久盘镜像路径
DISK_SIZE_MB 64 持久盘大小(MB)
FF_CACHE /home/zmn/.cache/fastfetch fastfetch 下载/构建缓存
FF_VER 2.68.1 fastfetch 版本
APK_CACHE /home/zmn/.cache/apk apk 静态包/密钥下载缓存
ALPINE_MIRROR http://mirrors.tuna.tsinghua.edu.cn/alpine Alpine 仓库镜像(国内源;写入 etc/apk/repositories)

手动构建步骤(原理)

1. 下载并解压内核

cd /home/zmn
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.12.tar.xz
tar -xf linux-6.12.tar.xz

以上是首次手工获取;之后升级内核直接用 ./build.sh kernel-up (自动查最新 6.12.x 点版本、携带 .config 换树、重编 + 刷模块 + 重打包)。

2. 配置内核

cd /home/zmn/linux-6.12
make ARCH=x86_64 defconfig
# 叠加最小化/硬化片段
# (build.sh 内部用 awk 合并 kernel-minimal.fragment,效果等价于 merge_config)
make ARCH=x86_64 olddefconfig

相对 x86_64_defconfig 的关键改动(见 kernel-minimal.fragment):

类别 配置项 作用
安全 # CONFIG_SECURITY_SELINUX is not set rootfs 无 SELinux 策略,默认关闭避免启动告警
硬化 CONFIG_FORTIFY_SOURCE=y / HARDENED_USERCOPY=y / SLAB_FREELIST_HARDENED=y 内存与拷贝加固
硬化 # CONFIG_DEVMEM / # CONFIG_PROC_KCORE 关闭 /dev/mem、/proc/kcore 攻击面
可观测 CONFIG_IKCONFIG=y + IKCONFIG_PROC=y 运行时 /proc/config.gz
命名空间 CONFIG_USER_NS=y 支持 unshare -U
调试裁剪 关闭 DEBUG_FS / FTRACE / KPROBES / KEXEC 减小体积与攻击面
驱动裁剪 关闭 Wireless / USB host / SCSI / SATA / CDROM / NFS / 9P / VFAT QEMU 串口启动用不到
保留 VIRTIO_BLK/NET/PCI、E1000、EXT4_FS、串口控制台 可选网卡/磁盘仍可用

验证:

grep -E 'CONFIG_(SECURITY_SELINUX|IKCONFIG|USER_NS|FORTIFY_SOURCE|DEVMEM|VIRTIO_BLK)=' .config

3. 编译内核

cd /home/zmn/linux-6.12
make ARCH=x86_64 CC=gcc-14 -j$(nproc)
# 成功标志:Kernel: arch/x86/boot/bzImage is ready
ls -la arch/x86/boot/bzImage

4. 配置 BusyBox

cd /home/zmn/busybox
make defconfig
# 静态链接(initramfs 无 /lib 动态加载器)
sed -i 's/^# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
sed -i 's/^CONFIG_STATIC=.*/CONFIG_STATIC=y/' .config
# 禁用 tc(新版内核头文件已删除 CBQ 结构体,否则编译失败)
sed -i 's/^CONFIG_TC=y/# CONFIG_TC is not set/' .config
# 叠加裁剪片段(关闭 telnetd/httpd/ftpd/i2c/ubi 等)
# 见 busybox-minimal.fragment,由 build.sh 自动合并
make oldconfig </dev/null
grep -E '^CONFIG_STATIC=|^# CONFIG_TC is not set' .config

5. 编译并安装 BusyBox

cd /home/zmn/busybox
make CC=gcc-14 -j$(nproc)
file busybox   # 必须显示 statically linked
make CONFIG_PREFIX=/home/zmn/rootfs install

6. rootfs 基础文件

/etc 至少包含(仓库中已提供):

文件 作用
etc/passwd 用户名/UID/shell(login 依赖);root 初始 x(口令在 shadow)
etc/group 组信息(含 users)
etc/shadow 口令;root 初始 * 锁定,首次 setup 时设置
etc/hostname 主机名
etc/hosts 本地解析
etc/resolv.conf DNS(有网时可用)
etc/os-release 发行版标识(fastfetch 的 OS 字段读取此处)
etc/profile 登录 shell 的 PATH/PS1(不覆盖 HOME);交互登录时清屏并运行 fastfetch
bin/mylogin 自定义登录程序:密码逐字显示 *(mylogin.c 编译)
bin/sudo 轻量 sudo:验证当前用户密码(显示 *)后以 root 执行;需在 wheel 组
bin/busybox setuid root(chmod 4755),否则 su 不可用
etc/issue / motd 登录前提示
etc/init.d/rcS 可选(busybox init 路径)

安全:出厂 rootfs 不带空口令;用户/ root 密码在首次开机交互创建,
并写入持久盘(若挂载了 disk.img)。

7. init 脚本要点(首次登录 / 持久化)

当前 /init 完成:

  1. 挂载 proc、sysfs、devtmpfs、devpts、tmpfs → /tmp 与 /run
  2. 兜底创建 console/ttyS0/null/urandom 节点;exec 到 console 作为 stdio
  3. 拉起 lo 与所有网卡;读 hostname;depmod;syslogd/klogd
  4. 持久盘:等待 /dev/vda,挂载到 /data(必要时 mkfs.ext2);
    将 /data/etc bind 覆盖 /etc → 用户与口令重启不丢
  5. 首次开机向导(无 /etc/.user_setup_done 时):
    • 先设置 root 密码(两次确认,≥6 位;逐字显示 *)
    • 再输入用户名(校验字符集,禁止 root/重复)并设置用户密码(同样显示 *)
    • chpasswd / adduser 写入;用户加入 wheel(供 sudo);写标记文件
  6. 登录循环:setsid cttyhack /bin/mylogin(自定义登录,密码显示 *)
    缺 mylogin 时回退 BusyBox getty+login(密码不可见)
    不直接进 root shell

无 disk.img 时仍可 setup,但 /etc 在 RAM 中,重启会重新进入创建向导。

7.1 首次开机流程(验收)

Run /init as init process
  Welcome to DreamLife Linux (BusyBox)
  Storage: /dev/vda -> /data (persistent /etc)
==============================================
  First boot: set root password, then create a user
==============================================
Set the root account password (needed for su / root login).
Root password: ******
Confirm Root password: ******
Root password set.

Username: alice
Password: *******
Confirm Password: *******

Created user 'alice'.
DreamLife-linux login: alice
Password: *******
[alice@DreamLife-linux ~]$   # 自动清屏 + fastfetch

之后重启(disk.img 在):跳过向导,直接 DreamLife-linux login:。

登录成功后 shell 读取 /etc/profile:清屏并自动运行 fastfetch,再进入提示符。

注意:/etc 在有盘时 bind 自 /data/etc。若旧 disk.img 里仍是旧 profile,
需 rm disk.img && ./build.sh disk 重置,或手动改 /data/etc/profile。

8. 安装内核模块到 rootfs

make -C linux-6.12 modules_install INSTALL_MOD_PATH=/home/zmn/rootfs
depmod -b /home/zmn/rootfs 6.12.0

内核中保持 =m 的选项(如 netfilter NAT 扩展)必须装进 /lib/modules,否则 modprobe 无效。 build.sh 的 rootfs/busybox 步骤会自动执行上述命令。

8.1 安装 fastfetch(musl 动态链接)

./build.sh fastfetch

step_fastfetch 流程(缓存于 FF_CACHE,避免重复下载):

  1. 下载官方 fastfetch-musl-amd64.tar.gz(FF_VER)
  2. strip 后装入 rootfs/bin/fastfetch
  3. 下载 musl 1.2.5 源码本地编译,取 libc.so 装为:
    • rootfs/lib/libc.musl-x86_64.so.1
    • rootfs/lib/ld-musl-x86_64.so.1 → 符号链接到上者
  4. 用 loader 直接执行 --version 做冒烟测试
  5. 随后 step_initramfs 重新打包

登录后运行:

[alice@DreamLife-linux ~]$ fastfetch
        alice@DreamLife-linux
        ---------------------
        OS: DreamLife Linux (BusyBox) x86_64
        Kernel: Linux 6.12.0
        ...

说明:根文件系统无 glibc/ld-linux,故采用 musl 官方二进制 + 自带 musl loader。 fastfetch 部分高级模块依赖 dlopen 加载宿主共享库,在本最小系统中会自动降级/跳过。

8.2 安装包管理器 dpkl(apt 风格,底层 apk)+ 网络(DHCP)

./build.sh apk

step_apk 流程:

  1. 下载 APKINDEX.tar.gz 解析当前版本号(apk-tools-static、alpine-keys)
  2. 下载两个 .apk(本质是 tar.gz)到 APK_CACHE 并解包:
    • sbin/apk.static → rootfs/usr/libexec/apk(Alpine 官方静态二进制, 移出 PATH;本系统已带 musl loader 故可直接运行)
    • etc/apk/keys/*.rsa.pub → rootfs/etc/apk/keys/(仓库签名公钥)
  3. 写 rootfs/sbin/dpkl:apt 风格前端(见下方用法表), 并建软链 sbin/apk → dpkl(旧习惯/apk 原生命令透传兼容)
  4. 写 rootfs/etc/apk/repositories(main + community,纯 HTTP; 默认国内源清华 TUNA,可用 ALPINE_MIRROR 换成阿里云/华为云等,或官方 http://dl-cdn.alpinelinux.org/alpine)
  5. 预建包数据库:/usr/libexec/apk --root rootfs --arch x86_64 add --initdb --usermode (生成 lib/apk/db/{installed,scripts.tar.gz,triggers} 与 etc/apk/{arch,world}, 否则 guest 内首次使用会报 Failed to open apk database)
  6. 写 rootfs/usr/share/udhcpc/default.script(BusyBox DHCP 钩子,见下)
  7. 处理 etc/hosts:仅当镜像是官方 dl-cdn 时固定可达 IP; 用国内镜像时自动清掉历史固定行(见“已知网络坑”)

网络:QEMU 命令已默认带用户模式网卡 (-netdev user,id=n0 -device virtio-net-pci,netdev=n0)。init 挂盘后后台执行 udhcpc -i eth0,脚本下发 10.0.2.15/24、网关 10.0.2.2,并把 DNS 10.0.2.3 写入 /etc/resolv.conf(随 /etc bind 持久化)。

持久化策略(init 的 PERSIST=1 分支:首启种子 + bind 挂载):

目录 首启种子 说明
/etc → /data/etc cp -a /etc/. 账号、仓库、DNS
/usr → /data/usr cp -a /usr/. apk 安装的可执行文件/库
/var → /data/var cp -a /var/. apk 缓存、日志
/lib/apk → /data/lib/apk cp -a /lib/apk/. 只绑 apk 数据库;/lib 其余部分保持 initramfs 版本(内核模块/加载器不被磁盘版覆盖)

镜像选择(默认国内源):

  • 默认 http://mirrors.tuna.tsinghua.edu.cn/alpine(清华 TUNA,纯 HTTP 直连, guest 内 busybox wget 无需 CA 证书;阿里云、华为云亦支持纯 HTTP,实测均 200)。
  • 本网络可用性实测:TUNA / 阿里云 / 华为云 纯 HTTP 200;中科大 USTC 403。
  • 换源:ALPINE_MIRROR=http://mirrors.aliyun.com/alpine ./build.sh apk 后重打包。 换回官方源时 step_apk 会自动补 hosts 固定行,换用国内镜像时自动清除。

已知网络坑(切回官方源时才会遇到):

  • dl-cdn.alpinelinux.org(fastly)的 DNS 会给出若干从本网络黑洞的 IP (23.235.x、104.156.x 不通 80 端口),apk 先撞上会超时失败 → step_apk 检测到镜像为 dl-cdn 时自动把两个可达 IP 写入 etc/hosts (musl 先查 hosts 再 DNS)。
  • init 里把 tcp_syn_retries 调成 2(黑洞 SYN 快速失败;默认要等 ~127s), 对国内源无副作用。
  • 速度参考:官方源在本网络仅约 25 KB/s,apk update(两个索引约 3MB)约 2 分钟; TUNA 实测两个索引各约 1 秒(apk update 秒级完成)。

guest 内用法(dpkl = apt 风格命令,底层为 Alpine apk):

dpkl(apt 风格) 对应 apt 命令 实际执行
dpkl update apt update apk update(刷新索引)
dpkl upgrade apt upgrade apk upgrade
dpkl install [-y] <pkg>... apt install apk add <pkg>...
dpkl remove [-y] <pkg>... apt remove apk del <pkg>...
dpkl purge <pkg>... apt purge apk del <pkg>...(别名)
dpkl search <kw> apt search apk search <kw>
dpkl show [pkg]... apt show apk info [pkg]...
dpkl list apt list apk info(列已安装)
dpkl clean apt clean 清空 /var/cache/apk 下载缓存
dpkl version — 显示 dpkl, version 3.0.8-r0 ...
dpkl help apt --help 内置帮助
  • -y / --yes / --assume-yes / --no-install-recommends 会被接受并忽略 (apk 本来就是非交互的),照搬 apt 肌肉记忆不会报错。
  • apk 仍可用:它是指向 dpkl 的软链,apk add/del/info/... 等原生命令 原样透传;真正的 apk 二进制在 /usr/libexec/apk(不在 PATH)。
  • 典型流程:
dpkl update
dpkl install -y tree
dpkl list
dpkl remove tree

滚动更新(Arch 式,latest-stable 稳定分支):

日常两连击即可把系统推到最新:

dpkl update        # 刷新索引(TUNA 秒级)
dpkl upgrade       # 全量升级所有已安装包(dpkl full-upgrade / dist-upgrade 为其别名)
  • 会滚动的:apk 数据库管理的包(dpkl install 装的一切,含 tree、musl 及 依赖),始终跟 latest-stable 小版本走(当前 3.22.x 系)。
  • 不会滚动的(宿主/自编,apk 管不到):
    • 内核 → ./build.sh kernel-up:自动查 TUNA 最新 KERNEL_SERIES.x 点版本 (实测 6.12.0 → 6.12.111),下载 → 携带 .config 换源码树 → 重编 → 刷 /lib/modules → 重打 initramfs → 重启 QEMU 即生效(uname -r 变化); 已是最新则增量重编(~5 分钟)。./build.sh kernel 仅重编当前源码。 换到全镜像最新大版本:KERNEL_SERIES=7.2 ./build.sh kernel-up(自动用 v7.x 目录, 实测最新 7.2.8);回滚换回 KERNEL_SERIES=6.12 即可,旧 config 备份在 ~/.cache/kernel/config-*.bak。生效需退出 QEMU 重新启动(guest 内 reboot 复用启动时已载入 fw_cfg 的旧 bzImage),uname -r 验证。
    • BusyBox、mylogin、sudo、fastfetch、dpkl、init → ./build.sh <对应步骤>
  • musl 滚动安全(已实测):dpkl install 拉入的 apk 版 musl 会接管 /lib/ld-musl-x86_64.so.1,此时宿主编译的 fastfetch(musl 动态链接)仍正常 运行(测试项 fastfetch runs on apk-installed musl)。
  • 不要装这三个包:busybox、alpine-baselayout、apk-tools —— 会覆盖 自编 BusyBox / 重建标准目录结构,破坏本系统底座。
  • 64MB 小盘:升级/安装的 .apk 留在 /var/cache/apk,反复滚动后记得 dpkl clean(apt clean 同款)防撑满。

Alpine 包是 musl 动态链接,本系统 loader 路径兼容,可直接运行。 tree 会自动拉入 musl 依赖,两启实测均正常(boot1 的 alpine musl 写在 内存盘 /lib,boot2 回退 initramfs 自带 loader,musl ABI 兼容)。 与 BusyBox 直接冲突的包(busybox、alpine-baselayout 等)慎装。


9. 打包 initramfs(规范)

cd /home/zmn/rootfs
find . -print0 \
  | cpio --null -o -H newc --owner root:root --reproducible \
  | gzip -9 > /home/zmn/initramfs.cpio.gz
要点 原因
--owner root:root 解压后属主为 root,避免 uid=1000 文件导致权限问题
--reproducible + 稳定目录序 构建可复现
gzip -9 最大压缩
-H newc 内核要求的 cpio 格式
find . -print0 正确处理特殊文件名

验证:

ls -la initramfs.cpio.gz
zcat initramfs.cpio.gz | cpio -t | grep -E '^(\./)?init$'
zcat initramfs.cpio.gz | cpio -tv | head   # 属主应为 root root

10. QEMU 启动(推荐:带持久盘)

# build.sh 会在打包后自动生成 disk.img(64MB ext4)
qemu-system-x86_64 \
  -kernel /home/zmn/linux-6.12/arch/x86/boot/bzImage \
  -initrd /home/zmn/initramfs.cpio.gz \
  -drive file=/home/zmn/disk.img,if=virtio,format=raw \
  -netdev user,id=n0 -device virtio-net-pci,netdev=n0 \
  -append "console=ttyS0 quiet loglevel=3 rdinit=/init" \
  -display none -serial mon:stdio \
  -m 256M
参数 作用
-kernel bzImage
-initrd gzip 压缩的 cpio initramfs
-drive ...,if=virtio virtio-blk 持久盘 → 账号存 /etc(bind 自 /data/etc)
-netdev user ... virtio-net-pci 用户模式网卡(DHCP:10.0.2.15,网关/DNS 10.0.2.2/3)
console=ttyS0 内核日志走串口
quiet / loglevel=3 屏蔽内核启动跑码(dmesg 仍可查看);init 内再 dmesg -n 1
rdinit=/init 指定 initramfs 中的 PID 1
-display none 关闭 VGA 窗口 → SeaBIOS 横幅不再转到终端
-serial mon:stdio 串口接到终端;Ctrl+A C 进 QEMU monitor,quit 退出

可选:

# 加速(宿主支持 KVM 时)
  -enable-kvm -cpu host

启动成功标志:

  DreamLife Linux  BusyBox · starting   ← 开机动画(spinner)

  Welcome to DreamLife Linux (BusyBox)
  Storage: /dev/vda -> /data (persistent /etc /usr /var /lib/apk)
==============================================
  First boot: create a user and set passwords
==============================================
Username: ...
DreamLife-linux login:

登录后:

[alice@DreamLife-linux /]$ id
uid=1000(alice) gid=1000(alice) ...

此时应具备:

  • 首次出现创建向导;完成后回到 login:(不直进 shell)
  • 无 job control turned off 警告
  • hostname 显示 DreamLife-linux
  • ps/id 显示用户名(/etc/passwd 生效)
  • zcat /proc/config.gz、ls /lib/modules/$(uname -r) 可用

重置账号/已装软件包:关机后 rm -f /home/zmn/disk.img,再 ./build.sh disk (或全量)。注意:apk 包数据库与装进 /usr 的包也在盘上,删盘一并重置。

退出:guest 内 poweroff,或宿主 Ctrl+A 再 C 再 quit。


目录与文件清单(rootfs 必备)

rootfs/
├── init                 # 自定义 PID 1(可执行)
├── bin/busybox          # 静态 busybox + 符号链接
├── bin/fastfetch        # musl 动态 fastfetch(step_fastfetch 安装)
├── bin/mylogin  bin/sudo
├── sbin/dpkl            # apt 风格包管理前端(step_apk 安装)
├── sbin/apk             # 软链 → dpkl(旧命令透传兼容)
├── usr/libexec/apk      # apk.static 真身(不在 PATH,dpkl 内部调用)
├── sbin/telinit         # telinit 垫片 → poweroff/reboot -f
├── sbin/poweroff 等     # wrapper:busybox poweroff -f(见故障排查)
├── usr/share/udhcpc/default.script  # DHCP 钩子(step_apk 写入)
├── sbin/  usr/bin/  usr/sbin/
├── etc/
│   ├── passwd  group  shadow  hostname  hosts   # 用官方源时才含 dl-cdn IP 固定
│   ├── resolv.conf  profile  fstab  issue  motd  os-release
│   ├── apk/   # repositories + keys/(Alpine 签名公钥)+ arch + world
│   ├── inittab          # 可选(busybox init 路径)
│   └── init.d/rcS
├── lib/apk/db/          # 预建 apk 包数据库(installed/triggers/scripts.tar.gz)
├── lib/modules/6.12.0/  # .ko + modules.dep
├── lib/ld-musl-x86_64.so.1  # musl 动态加载器(fastfetch/apk 用)
├── lib/libc.musl-x86_64.so.1
├── proc/ sys/ dev/ dev/pts/ tmp/ run/ root/ var/log/

故障排查

只有闪烁光标 / 无任何输出

序号 检查 命令 修复
1 bzImage 存在 ls arch/x86/boot/bzImage 重编内核
2 串口控制台 grep SERIAL_8250_CONSOLE .config 用 defconfig 起底
3 initrd 支持 grep BLK_DEV_INITRD .config 开启 BLK_DEV_INITRD
4 busybox 静态 file rootfs/bin/busybox CONFIG_STATIC=y 重编
5 init 可执行 ls -la rootfs/init chmod +x
6 打包含 init zcat initramfs.cpio.gz | cpio -t | grep init 重新打包

常见错误对照

现象 / 报错 原因 解决
can't access tty; job control turned off 未挂 devpts 或未用 cttyhack 检查 /init 是否含 devpts + setsid cttyhack
tc.c: 'TCA_CBQ_MAX' undeclared CONFIG_TC=y 与新内核头不兼容 禁用 CONFIG_TC
内核 panic No working init found initramfs 缺 init 或路径错 检查 rdinit= 与权限
SELinux 启动刷大量 AVC/无 policy 告警 默认启用 SELinux 但无策略 # CONFIG_SECURITY_SELINUX is not set 后重编
modprobe: can't change directory to ... 未 modules_install make modules_install INSTALL_MOD_PATH=rootfs
ps/id 显示数字 UID 缺 /etc/passwd 放入 rootfs/etc/passwd
zcat /proc/config.gz 不存在 未开 IKCONFIG CONFIG_IKCONFIG=y + IKCONFIG_PROC=y
initramfs 文件属主非 root 打包未加 --owner cpio --owner root:root
无线/USB 等驱动“编了但 QEMU 用不到” defconfig 过大 叠加 kernel-minimal.fragment

apk / 网络 / 关机

现象 / 报错 原因 解决
dpkl: command not found 未执行 apk 步骤 ./build.sh apk 后重打包
dpkl upgrade 后 uname -r 不变 内核不归 apk 管(QEMU -kernel 从宿主加载 bzImage) 宿主跑 ./build.sh kernel-up,然后重启 QEMU
Failed to open apk database 镜像里没有预建数据库 ./build.sh apk(内部 --initdb)后重打包
apk update: Operation timed out 或一直卡住 镜像是官方 dl-cdn 且 DNS 给出黑洞 IP ./build.sh apk(默认 TUNA 国内源,无需固定;用 dl-cdn 时会自动写 etc/hosts 固定行);老 disk 删掉重来或 root 改 guest /etc/hosts
apk update 花 1~2 分钟 镜像是官方源且出网慢(实测约 25 KB/s) 换国内源:ALPINE_MIRROR=http://mirrors.tuna.tsinghua.edu.cn/alpine ./build.sh apk(TUNA/阿里云/华为云均实测可用,USTC 403)
guest 无 IP / DNS 不通 QEMU 没加网卡,或 udhcpc 未跑 加 -netdev user,id=n0 -device virtio-net-pci,netdev=n0;root 下手动 udhcpc -i eth0
poweroff 立刻回到提示符、QEMU 不退出 老镜像的 poweroff 只给 PID1 发信号(自定义 init 不处理,信号被内核丢弃) ./build.sh rootfs 重打包(sbin/poweroff 已换成 busybox poweroff -f wrapper);应急 poweroff -f

体积参考(本仓库一次构建)

产物 约略大小
bzImage(精简后) ~10 MB
busybox(静态) ~2.4 MB
fastfetch(musl strip 后) ~2.9 MB(+ musl libc ~0.8 MB)
apk(静态 apk.static) ~5.2 MB
initramfs.cpio.gz ~5.9 MB
disk.img(64MB ext4) 64 MB(空洞文件,实际占用视写入量)

验收清单

  • ./build.sh 可一键完成全部步骤
  • rootfs 具备 /etc 基础文件
  • init 挂载 devpts/tmpfs,shell 支持 job control
  • 内核关闭 SELinux,开启 IKCONFIG/USER_NS/常见硬化
  • =m 模块安装到 /lib/modules 并生成 modules.dep
  • BusyBox 静态链接,裁剪无用网络服务
  • 打包 --owner root:root + gzip -9
  • 首次开机交互创建用户 + root 密码
  • 创建后回到登录提示符,用新用户 / root 登录
  • disk.img 持久化 /etc:重启跳过创建向导
  • 无 disk.img 时行为可预期(每次重启重新 setup)
  • 登录后 fastfetch 可用,OS 显示 DreamLife Linux
  • 登录后自动清屏并展示 fastfetch
  • 密码输入(setup + login + sudo)逐字显示 *,不回显明文
  • sudo <cmd> 可用(wheel 组 + 当前用户密码);su 可用(busybox SUID)
  • 无 SeaBIOS/内核跑码,开机动画后进欢迎页
  • ./build.sh apk:装 apk(静态)+ 签名密钥 + 仓库 + 预建包数据库 + DHCP 脚本
  • 开机自动 DHCP:10.0.2.15/24、网关 10.0.2.2、DNS 写入 resolv.conf(持久)
  • dpkl update + dpkl install -y tree 成功(apt 风格),tree(musl 动态)可执行
  • dpkl search/show/list/version 可用;apk 软链透传兼容(apk info 正常)
  • 滚动更新:dpkl upgrade 全量升级 exit 0;dpkl clean 清缓存可用
  • 内核滚动:./build.sh kernel-up 6.12.0 → 6.12.111 → 7.2.8(KERNEL_SERIES=7.2 跨大版本,配置自动迁移),uname -r 对账通过
  • apk 版 musl 接管 loader 后 fastfetch(宿主 musl 二进制)仍正常运行
  • /usr /var /lib/apk bind 持久化:重启后 tree 与包数据库仍在(dpkl list)
  • poweroff 正常关机退出 QEMU,reboot 正常重启
  • 自动化验收 bash run_apk_test.sh → 28/28 passed

附录:关闭 QEMU 的其他方式

  • guest 内:poweroff / reboot / halt(sbin/* 已换成强制 -f wrapper, 直接走 reboot(2) 系统调用,不依赖 init 信号;telinit 垫片兜底)
  • 宿主:Ctrl+A 然后 C 然后 quit

附录:常见登录问题

现象 原因 解决
每次重启都要创建用户 未挂 disk.img 加 -drive file=disk.img,if=virtio,format=raw
login: 后密码被拒 向导中密码不一致/太短;或 root 仍为 * 重跑 setup;或 rm disk.img 重来
无 DreamLife-linux login: init 被截断/未更新 ./build.sh rootfs 后确认 init 含 first_boot_setup
adduser: not found 未装 loginutils 确认 CONFIG_ADDUSER=y 后 ./build.sh busybox
fastfetch: not found 未执行 fastfetch 步骤 ./build.sh fastfetch(会自动下载并重新打包)
fastfetch: can't load interpreter / loader 报错 缺 musl loader 确认 rootfs/lib/ld-musl-x86_64.so.1 存在后重新打包
密码输入完全不可见(无 *) 使用了旧 getty/login 或旧 profile ./build.sh rootfs 后确认有 bin/mylogin;rm disk.img 重置
Login incorrect 但密码正确 mylogin 未编译进 initramfs 或 shadow 解析旧版本 ./build.sh mylogin && ./build.sh initramfs
sudo: not found 未编译 sudo 或未重新打包 ./build.sh mylogin && ./build.sh initramfs
sudo: ... is not in the wheel group 老 disk 上的用户未加入 wheel guest 内 root 执行 addgroup <user> wheel,或 rm disk.img 重置
su: must be suid to work properly busybox 未 SUID ./build.sh mylogin(会 chmod 4755 busybox)后重打包
终端仍出现 SeaBIOS (...) 仍用 -nographic 改为 -display none -serial mon:stdio
S
Description
最小Linux系统编译
Readme MIT
42 KiB
0 Stars 1 Watchers 0 Forks