4. BusyBox编译与安装:make编译、make install安装、生成_install目录结构分析
好,咱们接着往下走。上一节我们把BusyBox的配置项都捋了一遍,现在配置已经选好了,接下来就是真刀真枪地编译和安装了。
说实话,编译BusyBox本身并不复杂,但很多人栽在细节上。我见过不少新手,make完了直接make install,结果发现系统都起不来。嗯,这里面的门道,我慢慢跟你说。
4.1 make编译:不是简单的敲个命令
配置完成后,在源码根目录下直接执行:
make
就这么简单?对,但也不完全对。我个人习惯在编译前先看一眼编译环境:
make V=1 # 显示详细的编译信息
为什么要加V=1?说白了,就是让你看到编译器到底在干什么。如果编译报错,你能第一时间定位到是哪个源文件出了问题。我在项目中遇到过好几次,因为交叉编译器版本不对,导致某些系统调用找不到,加了V=1一眼就看出问题所在。
如果你想多核加速编译,可以这样:
make -j$(nproc)
nproc会返回你机器的CPU核心数。比如8核机器,就是make -j8。但要注意,如果你是在虚拟机里编译,或者内存不大,-j开太高反而会卡死。我建议保守一点,用-j4或者-j2。
make CROSS_COMPILE=arm-linux-gnueabihf-
否则默认会用gcc编译,生成的是x86的可执行文件,放到ARM板子上根本跑不起来。
4.2 make install:安装到哪是个问题
编译成功后,下一步就是安装。默认情况下:
make install
这个命令会把BusyBox安装到/usr/local目录下。但你想,我们做嵌入式系统,怎么可能装到宿主机上?所以一定要指定安装路径:
make install CONFIG_PREFIX=/path/to/your/rootfs
CONFIG_PREFIX这个变量,就是告诉BusyBox:嘿,把生成的文件都放到这个目录下去。我一般习惯创建一个空目录,比如~/my_rootfs,然后指定到这里。
rm -rf ~/my_rootfs && mkdir ~/my_rootfs
make install CONFIG_PREFIX=~/my_rootfs
安装完成后,你会看到类似这样的输出:
/home/user/my_rootfs/bin/busybox
/home/user/my_rootfs/sbin/busybox
/home/user/my_rootfs/usr/bin/busybox
...
嗯,这里要注意,BusyBox其实只生成一个可执行文件——busybox。其他那些ls、cp、cat之类的,都是指向busybox的符号链接。
4.3 _install目录结构分析:麻雀虽小五脏俱全
安装完成后,我们来看看_install目录(如果你没指定CONFIG_PREFIX,默认就是_install)里到底有什么。
先执行tree命令看看整体结构:
tree -L 2 ~/my_rootfs
输出大概长这样:
my_rootfs/
├── bin
│ ├── busybox
│ ├── cat -> busybox
│ ├── chmod -> busybox
│ ├── cp -> busybox
│ ├── date -> busybox
│ ├── echo -> busybox
│ ├── grep -> busybox
│ ├── gzip -> busybox
│ ├── kill -> busybox
│ ├── ln -> busybox
│ ├── ls -> busybox
│ ├── mkdir -> busybox
│ ├── mount -> busybox
│ ├── mv -> busybox
│ ├── ps -> busybox
│ ├── rm -> busybox
│ ├── sh -> busybox
│ ├── sleep -> busybox
│ ├── tar -> busybox
│ ├── touch -> busybox
│ └── ...
├── sbin
│ ├── busybox
│ ├── fdisk -> busybox
│ ├── fsck -> busybox
│ ├── halt -> busybox
│ ├── ifconfig -> busybox
│ ├── init -> busybox
│ ├── insmod -> busybox
│ ├── mdev -> busybox
│ ├── mkfs.ext2 -> busybox
│ ├── reboot -> busybox
│ ├── route -> busybox
│ ├── swapon -> busybox
│ └── ...
├── usr
│ ├── bin
│ │ ├── busybox
│ │ ├── [ -> busybox
│ │ ├── awk -> busybox
│ │ ├── cut -> busybox
│ │ ├── find -> busybox
│ │ ├── head -> busybox
│ │ ├── hexdump -> busybox
│ │ ├── tail -> busybox
│ │ ├── test -> busybox
│ │ ├── top -> busybox
│ │ ├── uniq -> busybox
│ │ ├── wc -> busybox
│ │ ├── wget -> busybox
│ │ └── ...
│ └── sbin
│ ├── busybox
│ ├── chroot -> busybox
│ ├── telnetd -> busybox
│ ├── udhcpd -> busybox
│ └── ...
└── linuxrc -> bin/busybox
这个结构,说白了就是标准Linux文件系统的精简版。我们来拆解一下:
4.3.1 bin/ 目录
存放的是最基本的用户命令。比如ls、cp、mv、cat这些,系统启动后用户马上就会用到的。注意看,sh也在这里,也就是说BusyBox自带了一个shell,虽然功能比不上bash,但嵌入式环境完全够用。
4.3.2 sbin/ 目录
系统管理命令,普通用户一般用不到,需要root权限。比如init(系统初始化进程)、ifconfig(网络配置)、fdisk(磁盘分区)、reboot(重启)等。我刚开始做嵌入式时,经常搞混bin和sbin的区别,后来记住一句话:sbin是给系统用的,bin是给人用的。
4.3.3 usr/ 目录
这个目录下又分了bin和sbin。从功能上讲,usr/bin放的是用户级的附加命令,比如awk、find、top这些;usr/sbin放的是系统级的附加服务,比如telnetd(telnet服务器)、udhcpd(DHCP服务器)。
你可能会问:为什么要把命令分到bin和usr/bin两个地方?其实这是历史遗留问题。早期Unix系统磁盘很小,/usr可以挂载到另一块磁盘上。现在嵌入式系统里,很多人干脆把bin和usr/bin合并了。我个人习惯是,如果根文件系统是只读的,就把所有命令都放到bin里,省事。
4.3.4 linuxrc 文件
这个文件很特殊。它是一个符号链接,指向bin/busybox。在系统启动时,内核会先执行linuxrc(如果存在),而不是/sbin/init。这通常用于initramfs或initrd场景,做一些早期的设备初始化工作。
busybox这一个文件。其他所有命令都是符号链接。这就是BusyBox的精髓——一个二进制文件,模拟上百个Linux命令。
4.4 验证安装是否成功
安装完了,怎么知道对不对?我一般会做两个检查:
- 检查busybox文件本身:用
file命令看看是不是目标架构的可执行文件。 - 检查符号链接:随机挑几个命令,用
ls -l看看它们是否指向了busybox。
# 检查文件类型
file ~/my_rootfs/bin/busybox
# 检查符号链接
ls -l ~/my_rootfs/bin/ls
ls -l ~/my_rootfs/sbin/init
ls -l ~/my_rootfs/usr/bin/top
如果一切正常,你会看到类似这样的输出:
bin/busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, stripped
bin/ls -> busybox
sbin/init -> busybox
usr/bin/top -> busybox
嗯,看到ARM字样,说明交叉编译成功了。如果显示的是x86-64,那你就要回头检查CROSS_COMPILE有没有设置对了。
busybox是动态链接的,但根文件系统里没有放动态链接库。解决办法有两个:要么编译成静态链接(配置时选上CONFIG_STATIC),要么把对应的.so文件拷到/lib目录下。
4.5 本章知识结构图
下面这张图,帮你理清编译安装的整个流程和目录结构的关系:
这张图把整个流程串起来了。从配置到编译,再到安装,最后生成四个核心目录/文件。你每次做根文件系统时,都可以拿这张图对照着检查,看自己走到哪一步了。
好了,编译安装这一步就讲到这里。记住:make只是手段,_install目录才是我们真正要的东西。下一节,我们会基于这个目录,开始构建完整的根文件系统。
ls -li命令查看_install目录下所有文件的inode号。你会发现,所有命令的inode号都是一样的——因为它们指向的是同一个busybox文件。这就是硬链接和符号链接的妙用。
可可精品资料库