29 KiB
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 完成:
- 挂载
proc、sysfs、devtmpfs、devpts、tmpfs→/tmp与/run - 兜底创建
console/ttyS0/null/urandom节点;exec到 console 作为 stdio - 拉起
lo与所有网卡;读 hostname;depmod;syslogd/klogd - 持久盘:等待
/dev/vda,挂载到/data(必要时mkfs.ext2);
将/data/etcbind 覆盖/etc→ 用户与口令重启不丢 - 首次开机向导(无
/etc/.user_setup_done时):- 先设置 root 密码(两次确认,≥6 位;逐字显示
*) - 再输入用户名(校验字符集,禁止
root/重复)并设置用户密码(同样显示*) chpasswd/adduser写入;用户加入wheel(供sudo);写标记文件
- 先设置 root 密码(两次确认,≥6 位;逐字显示
- 登录循环:
setsid cttyhack /bin/mylogin(自定义登录,密码显示*)
缺mylogin时回退 BusyBoxgetty+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,避免重复下载):
- 下载官方
fastfetch-musl-amd64.tar.gz(FF_VER) strip后装入rootfs/bin/fastfetch- 下载 musl
1.2.5源码本地编译,取libc.so装为:rootfs/lib/libc.musl-x86_64.so.1rootfs/lib/ld-musl-x86_64.so.1→ 符号链接到上者
- 用 loader 直接执行
--version做冒烟测试 - 随后
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 流程:
- 下载
APKINDEX.tar.gz解析当前版本号(apk-tools-static、alpine-keys) - 下载两个
.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/(仓库签名公钥)
- 写
rootfs/sbin/dpkl:apt 风格前端(见下方用法表), 并建软链sbin/apk→dpkl(旧习惯/apk原生命令透传兼容) - 写
rootfs/etc/apk/repositories(main + community,纯 HTTP; 默认国内源清华 TUNA,可用ALPINE_MIRROR换成阿里云/华为云等,或官方http://dl-cdn.alpinelinux.org/alpine) - 预建包数据库:
/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) - 写
rootfs/usr/share/udhcpc/default.script(BusyBox DHCP 钩子,见下) - 处理
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-linuxps/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-up6.12.0 → 6.12.111 → 7.2.8(KERNEL_SERIES=7.2跨大版本,配置自动迁移),uname -r对账通过 - apk 版 musl 接管 loader 后 fastfetch(宿主 musl 二进制)仍正常运行
/usr/var/lib/apkbind 持久化:重启后tree与包数据库仍在(dpkl list)poweroff正常关机退出 QEMU,reboot正常重启- 自动化验收
bash run_apk_test.sh→ 28/28 passed
附录:关闭 QEMU 的其他方式
- guest 内:
poweroff/reboot/halt(sbin/*已换成强制-fwrapper, 直接走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 |