Busybox构建最小Linux系统

 

前言说明

编译程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 安装所需程序
sudo apt install build-essential libncurses5-dev

# 获取源码包及解压的两种方式
# curl https://busybox.net/downloads/busybox-1.37.0.tar.bz2 | tar xjf -
wget https://busybox.net/downloads/busybox-1.37.0.tar.bz2
tar xvf busybox-1.37.0.tar.bz2

cd busybox-1.37.0
make defconfig # 生成默认的配置,最大配置:allyesconfig,最小配置:allnoconfig
make menuconfig # 进入图形化配置界面
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- # 其他架构也可更改Makefile中 ARCH ?= aarch64 和 CROSS_COMPILE ? 来编译
make -j8 # 编译后的程序为源码目录下的 busybox
make install # 默认部署在源码目录下的 _install 中
# make CONFIG_PREFIX=/home/leux/tmp/ install # 也可自定义根文件系统的部署路径

# 默认 make install 安装后的目录结构
leux@B650I:~/busybox-1.37.0$ tree _install/
_install/
├── bin
│   ├── arch -> busybox
│   ├── ......
│   └── zcip -> ../bin/busybox
├── linuxrc -> bin/busybox
├── sbin
│   ├── acpid -> ../bin/busybox
│   ├── ......
│   └── zcip -> ../bin/busybox
└── usr
├── bin
│   ├── [ -> ../../bin/busybox
│   ├── [[ -> ../../bin/busybox
│   ├── ascii -> ../../bin/busybox
│   ├── ......
│   └── yes -> ../../bin/busybox
└── sbin
├── addgroup -> ../../bin/busybox
   ├── ......
└── udhcpd -> ../../bin/busybox

6 directories, 404 files
leux@B650I:~/busybox-1.37.0$


# make menuconfig 中的部分选项介绍:
# 使用静态编译
Settings --->
--- Build Options
[*] Build static binary (no shared libs) (NEW)

# 添加Unicode宽字符支持
Settings --->
--- Library Tuning
[*] Support Unicode # 默认已支持Unicode
[*] Allow wide Unicode characters on output

编译内核

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 安装编译内核所需程序
sudo apt install build-essential flex bison bc libncurses5-dev libelf-dev libssl-dev

# 下载内核源码及解压
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.17.tar.xz
tar -xJvf linux-6.12.17.tar.xz && cd linux-6.12.17

# 使用默认配置编译完成后拷贝内核 bzImage 待用
make x86_64_defconfig
make menuconfig
make -j8
cp arch/x86/boot/bzImage /mnt/d/


# make menuconfig 中可能需要调整的选项:
General setup --->
----> [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support # 内核初始化支持initramfs和initrd
Device Drivers --->
[*] Block devices --->
<*> RAM block device support # 内核支持ram disk块设备驱动
(16) Default number of RAM disks # 注册的/dev/ram0块设备数量,默认是16则会有/dev/ram0~ram15
(65536) Default RAM disk size (kbytes) # RamDisk占用的最大内存,这里我写的是64M

# 启动后如果硬盘或分区找不到可把 SCSI device 和 ext4 等驱动打包到内核中

初始镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 从内核启动到内存镜像的简单流程如下,如需启动到硬盘系统请看后续其他教程
# Bootloader => bzimage => /sbin/init => /etc/inittab => /etc/init.d/rcS => /etc/fstab ...

# 进入busybox根目录
cd busybox-1.37.0/_install/

# 依照嵌入式Linux根文件系统结构,在根目录 _install 中创建其他目录
mkdir -p dev proc sys etc/init.d/ tmp lib var

# 内核默认会执行 /init ,而安装时生成的 linuxrc 在这没用则删除了
ln -s sbin/init init && rm linuxrc


# 执行 /init 时会读取 /etc/inittab 中的配置
cat > etc/inittab << EOF
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
EOF


# 配置文件 /etc/inittab 中首行指定系统启动后执行 /etc/init.d/rcS 脚本
cat > etc/init.d/rcS << EOF
#! /bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

/bin/mount -a
exec /sbin/init
EOF


# 脚本 /etc/init.d/rcS 要有执行权限,而 inittab 配置文件要可读
chmod a+x etc/init.d/rcS


# 在 rcS 脚本中的 /bin/mount -a 则会读取 /etc/fstab 配置来挂载系统分区
cat > etc/fstab << EOF
tmpfs /tmp tmpfs defaults 0 0
EOF


# 最后将其打包压缩成 initrd 待用
find . | cpio -o -H newc | gzip -9c > ../initrd.gz

# 这样制作的镜像虽然能够启动,但因为是运行在内存中的,所以全部更改重启后会丢失

启动系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 内核既可以自行编译也可用Debian的内核包linux-image-amd64中的:\data.tar\boot\vmlinuz-6.1.0-0.deb11.31-cloud-amd64
https://mirrors.aliyun.com/debian-security/pool/main/l/linux-signed-6.1-amd64/linux-image-6.1.0-0.deb11.31-cloud-amd64_6.1.128-1~deb11u1_amd64.deb

# 将编译好的 bzImage 与打包压缩好的 initrd 用 QEMU 启动
qemu-system-x86_64 -kernel bzImage -initrd initrd.gz

qemu-system-x86_64 -m 512M -smp 4 -kernel vmlinuz-6.1.0-31-amd64 -initrd initrd.gz --accel whpx,kernel-irqchip=off

qemu-system-x86_64 -kernel vmlinuz-6.1.0-0.deb11.31-cloud-amd64 -initrd initrd.gz -nographic -append "console=ttyS0 init=/linuxrc"

-kernel # 指定获取到的内核
-initrd # 指定制作的initrd
-nographic # 取消图形输出窗口
-append "console=ttyS0" # 将输出重定向到console,将会显示在标准输出stdio
--accel whpx,kernel-irqchip=off # 启用hyper-v加速,加 kernel-irqchip=off 可解决 whpx: injection failed, 问题

问题解决

  1. 启动后反复提示:can't open /dev/tty2: No such file or directory
1
2
3
# 在 /etc/inittab 中有【tty2::askfirst:-/bin/sh】这句导致的,删除这句前面的 tty2 
::askfirst:-/bin/sh

  1. 挂载配置文件 /etc/fstab 的加载流程
1
2
3
4
5
6
# /init -> /sbin/init	# 会读取 /etc/inittab
# /etc/inittab # 会执行 /etc/init.d/rcS
# /etc/init.d/rcS # 会读取 /etc/fstab
# cat /etc/fstab # rcS中执行 /bin/mount -a 时会读取该配置来挂载
tmpfs /tmp tmpfs defaults 0 0

  1. 路径 /dev/ 下没有相关节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 不需要执行这一句了,后面执行 mdev -s 时会扫描 /sys 自动生成
#cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/

# 在启动脚本 init 中添加命令 mdev -s 来扫描 /sys 自动生成
cat > init << EOF
#!/bin/busybox sh
mount -t proc none /proc
mount -t sysfs none /sys
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

exec /sbin/init
EOF