Files
DreamLife_Linux/README.md
T
2026-09-23 08:10:18 +00:00

360 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Linux 内核 + BusyBox 最小系统构建指南
> 基于 Linux 6.12 + BusyBox,使用 QEMU 启动的最小根文件系统。
> 本文档记录从零构建到成功进入 shell 的完整过程,包含故障排查。
---
## 环境准备
```bash
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
```
目录结构:
```
/home/zmn/
├── linux-6.12/ # 内核源码
├── busybox/ # BusyBox 源码
├── rootfs/ # 根文件系统
└── initramfs.cpio.gz # 打包后的 initramfs
```
---
## 第一步:下载并解压内核源码
```bash
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
```
---
## 第二步:配置内核
```bash
cd /home/zmn/linux-6.12
make x86_64_defconfig
```
### 关键配置项
`x86_64_defconfig` 已默认启用以下必需选项(可用 `grep` 验证):
| 配置项 | 作用 |
|--------|------|
| `CONFIG_SERIAL_8250=y` | 串口驱动 |
| `CONFIG_SERIAL_8250_CONSOLE=y` | 串口控制台(`console=ttyS0` 依赖此项) |
| `CONFIG_BLK_DEV_INITRD=y` | initramfs 支持(`-initrd` 依赖此项) |
| `CONFIG_RD_GZIP=y` | gzip 压缩的 initrd 解压支持 |
| `CONFIG_DEVTMPFS=y` | `/dev` 自动挂载 |
| `CONFIG_PRINTK=y` | 内核日志输出 |
验证:
```bash
grep -E 'CONFIG_(SERIAL_8250_CONSOLE|BLK_DEV_INITRD|RD_GZIP|DEVTMPFS|PRINTK)=' .config
```
> **注意**:不要用 `make tinyconfig`,它几乎关闭所有驱动,导致没有串口控制台输出,
> QEMU 只会显示一个闪烁的光标。
---
## 第三步:编译内核
```bash
cd /home/zmn/linux-6.12
make CC=gcc-14 -j$(nproc)
```
编译成功标志:
```
Kernel: arch/x86/boot/bzImage is ready (#1)
```
验证产物:
```bash
ls -la arch/x86/boot/bzImage
# 应输出类似:-rw-r--r-- 1 zmn zmn 13583360 ... bzImage
```
---
## 第四步:下载并配置 BusyBox
```bash
cd /home/zmn
git clone --depth 1 https://git.busybox.net/busybox
cd busybox
make defconfig
```
### 关键配置:静态编译
```bash
# 开启静态链接(不依赖 libc.so 等共享库)
sed -i 's/^# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
# 禁用 tc(新版内核头文件已删除 CBQ 结构体,会导致编译失败)
sed -i 's/^CONFIG_TC=y/# CONFIG_TC is not set/' .config
grep -E '^CONFIG_STATIC=|^CONFIG_TC=' .config
# 应输出:
# CONFIG_STATIC=y
# # CONFIG_TC is not set
```
> **为什么必须静态编译**:initramfs 中没有 `/lib`、`/lib64`,
> 动态链接的 BusyBox 在启动时会因找不到 `ld-linux-x86-64.so.2` 而失败,
> 内核 panic,屏幕上什么都看不到。
>
> **为什么必须禁用 tc**:`make defconfig` 默认开启 `CONFIG_TC=y`,
> 但 `networking/tc.c` 引用的 `TCA_CBQ_*` 等结构体已在新版内核头文件中删除,
> 会导致编译报错 `TCA_CBQ_MAX undeclared`。
---
## 第五步:编译 BusyBox
```bash
cd /home/zmn/busybox
make CC=gcc-14 -j$(nproc)
```
编译成功后验证:
```bash
file busybox
# 应输出:ELF 64-bit LSB executable, x86-64, ... statically linked
```
---
## 第六步:构建根文件系统
### 6.1 创建目录结构
```bash
mkdir -p /home/zmn/rootfs/{bin,sbin,etc,proc,sys,dev,tmp,root,usr/bin,usr/sbin}
```
### 6.2 安装 BusyBox
```bash
cd /home/zmn/busybox
make CONFIG_PREFIX=/home/zmn/rootfs install
```
验证:
```bash
file /home/zmn/rootfs/bin/busybox
# 应输出:statically linked
```
### 6.3 编写 init 脚本
用 `cat` 写入 `/home/zmn/rootfs/init`:
```bash
cat > /home/zmn/rootfs/init << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
hostname minimal-linux
exec /bin/sh
EOF
```
赋予执行权限:
```bash
chmod +x /home/zmn/rootfs/init
```
验证:
```bash
ls -la /home/zmn/rootfs/init
# 应输出:-rwxr-xr-x ... /home/zmn/rootfs/init
```
### 6.4 init 脚本说明
| 命令 | 作用 |
|------|------|
| `mount -t proc` | 挂载进程信息系统,`ps`、`/proc` 依赖它 |
| `mount -t sysfs` | 挂载 sysfs,`/sys` 依赖它 |
| `mount -t devtmpfs` | 挂载设备文件系统,自动创建 `/dev/ttyS0` 等设备节点 |
| `exec /bin/sh` | 用 shell 替换当前进程,进入交互式命令行 |
---
## 第七步:打包 initramfs
```bash
cd /home/zmn/rootfs
find . | cpio -o -H newc 2>/dev/null | gzip > /home/zmn/initramfs.cpio.gz
```
验证:
```bash
ls -la /home/zmn/initramfs.cpio.gz
zcat /home/zmn/initramfs.cpio.gz | cpio -t 2>/dev/null | grep '^init$'
# 应输出:init
```
---
## 第八步:QEMU 启动
```bash
qemu-system-x86_64 \
-kernel /home/zmn/linux-6.12/arch/x86/boot/bzImage \
-initrd /home/zmn/initramfs.cpio.gz \
-append "console=ttyS0 rdinit=/init" \
-nographic \
-m 256M
```
### 参数说明
| 参数 | 作用 |
|------|------|
| `-kernel` | 指定内核 bzImage 路径 |
| `-initrd` | 指定 initramfs 路径 |
| `console=ttyS0` | 内核日志输出到串口(配合 `-nographic` 显示在终端) |
| `rdinit=/init` | 指定 initramfs 中的初始进程为 `/init` |
| `-nographic` | 不使用图形窗口,串口重定向到当前终端 |
| `-m 256M` | 分配 256MB 内存 |
### 启动成功标志
滚动的内核日志结束后出现:
```
Run /init as init process
/bin/sh: can't access tty; job control turned off
~ #
```
`~ #` 即为 shell 提示符,此时可以输入命令:
```
~ # uname -r
6.12.0
~ # ls
bin etc linuxrc root sys usr
dev init proc sbin tmp
```
### 退出 QEMU
- 方法一:shell 中输入 `poweroff`
- 方法二:按 `Ctrl+A` 再按 `X`
---
## 故障排查
### 症状:只有一个闪烁的光标,其他什么都没有
按以下顺序排查:
| 序号 | 检查项 | 排查命令 | 修复方法 |
|------|--------|----------|----------|
| 1 | bzImage 是否存在 | `ls -la arch/x86/boot/bzImage` | 重新编译内核 |
| 2 | 是否启用了串口控制台 | `grep SERIAL_8250_CONSOLE .config` | 改用 `x86_64_defconfig` |
| 3 | 是否启用了 initrd 支持 | `grep BLK_DEV_INITRD .config` | 改用 `x86_64_defconfig` |
| 4 | BusyBox 是否静态链接 | `file rootfs/bin/busybox` | 开启 `CONFIG_STATIC=y` 重编 |
| 5 | init 是否存在且可执行 | `ls -la rootfs/init` | `chmod +x rootfs/init` |
| 6 | initramfs 是否包含 init | `zcat initramfs.cpio.gz \| cpio -t \| grep init` | 重新打包 |
### 常见错误对照
| 错误信息 | 原因 | 解决 |
|----------|------|------|
| `qemu: could not open kernel file` | bzImage 不存在 | 编译内核 |
| `Run /init as init process` 后无输出 | init 不可执行或 BusyBox 动态链接缺库 | 检查权限、改静态编译 |
| `tc.c: error: 'TCA_CBQ_MAX' undeclared` | `CONFIG_TC=y` 但内核已删除 CBQ 结构体 | 禁用 `CONFIG_TC` 后重编 |
| `busybox: cannot open ... No such file or directory` | 上次编译失败,二进制未生成 | 修复编译错误后重新 `make` |
| `can't access tty; job control turned off` | 正常现象,无需处理 | 忽略 |
| 内核 panic `No working init found` | initramfs 未加载或 init 路径错误 | 检查 `rdinit=` 和 `BLK_DEV_INITRD` |
| 终端完全没有内核日志 | 缺少串口控制台支持 | 检查 `SERIAL_8250_CONSOLE` |
---
## 完整构建脚本
将以下内容保存为 `build.sh`,可一键完成整个构建流程:
```bash
#!/bin/bash
set -e
KERNEL_DIR=/home/zmn/linux-6.12
BB_DIR=/home/zmn/busybox
ROOTFS=/home/zmn/rootfs
INITRAMFS=/home/zmn/initramfs.cpio.gz
echo "=== [1/6] 配置内核 ==="
cd "$KERNEL_DIR"
make x86_64_defconfig
echo "=== [2/6] 编译内核 ==="
make CC=gcc-14 -j"$(nproc)"
test -f arch/x86/boot/bzImage && echo "bzImage OK"
echo "=== [3/6] 配置 BusyBox(静态编译,禁用 tc)==="
cd "$BB_DIR"
make defconfig
sed -i 's/^# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
sed -i 's/^CONFIG_TC=y/# CONFIG_TC is not set/' .config
grep -E '^CONFIG_STATIC=|^CONFIG_TC=' .config
echo "=== [4/6] 编译 BusyBox ==="
make CC=gcc-14 -j"$(nproc)"
file busybox | grep -q "statically linked" && echo "static OK"
echo "=== [5/6] 安装到 rootfs 并写 init ==="
mkdir -p "$ROOTFS"/{bin,sbin,etc,proc,sys,dev,tmp,root,usr/bin,usr/sbin}
make CONFIG_PREFIX="$ROOTFS" install
cat > "$ROOTFS/init" << 'INITEOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
hostname minimal-linux
exec /bin/sh
INITEOF
chmod +x "$ROOTFS/init"
echo "=== [6/6] 打包 initramfs ==="
cd "$ROOTFS"
find . | cpio -o -H newc 2>/dev/null | gzip > "$INITRAMFS"
echo "Done: $INITRAMFS"
echo ""
echo "启动命令:"
echo "qemu-system-x86_64 -kernel $KERNEL_DIR/arch/x86/boot/bzImage -initrd $INITRAMFS -append \"console=ttyS0 rdinit=/init\" -nographic -m 256M"
```
运行:
```bash
chmod +x build.sh
./build.sh
```