1. 概述
BusyBox 是一款将数百个常用 Unix 工具集成于单一可执行文件中的软件套件,主要面向存储与内存资源受限的嵌入式系统。其官方定位为 The Swiss Army Knife of Embedded Linux。
在典型的嵌入式 Linux 构建流程中,BusyBox 作为根文件系统(rootfs)的基础组件被广泛使用:
- Buildroot:默认采用 BusyBox 构建最小化用户空间;
- OpenWrt:路由器固件的根文件系统基于 BusyBox;
- Alpine Linux :Docker 基础镜像(约 5 MB)中的
/bin/sh指向 BusyBox; - Yocto Project:多个版本分支均将 BusyBox 作为默认工具集。
在常规 Linux 发行版中,ls、cp、cat 等命令通常以独立二进制文件形式分布于 /bin、/usr/bin 目录下。BusyBox 则通过 多调用二进制(multi-call binary) 机制,将上述命令以 applet 形式内聚于单一 busybox 二进制中。系统通过符号链接(symlink)将各命令名映射至 busybox,程序启动时依据 argv[0] 判断应执行的 applet 功能。
2. 技术机制
2.1 Multi-Call Binary 与 Applet 调度
BusyBox 的基本机制为 multi-call binary。其内部维护一张 applets[] 映射表,存储命令名与对应函数指针的关联。程序入口提取 argv[0] 的基名(basename),通过查表跳转至目标 applet 的实现函数。该方式避免了独立进程的重复加载开销,并使得代码段(text segment)在所有 applet 间共享。
2.2 符号链接与 Standalone Shell
安装 BusyBox 时,通常为其支持的每个命令创建指向 busybox 的符号链接。此外,BusyBox 提供 standalone shell 模式:在该模式下,用户无需建立符号链接,直接进入 BusyBox 自带的 shell(ash 或 hush),即可调用所有内建命令。该模式常用于 initramfs 早期引导阶段------此时根文件系统尚未完全挂载,依赖符号链接的传统方式不可用。
2.3 配置与裁剪
BusyBox 采用与 Linux 内核一致的 Kconfig 配置系统,通过 make menuconfig 进行图形化裁剪。开发者可按需启用或禁用特定 applet,从而精确控制最终二进制体积。配置类别涵盖 Archival、Coreutils、Editors、Networking、Shell 等数十个模块。
3. 内部命令与外部命令
3.1 基本概念
在 Linux 系统中,Shell 接收的指令可分为两类:
| 类型 | 定义 | 执行方式 | 示例 |
|---|---|---|---|
| 内部命令(builtin) | 由 Shell 解释器自身实现的命令,不调用外部可执行文件 | 在当前 Shell 进程内执行,不创建子进程 | cd、echo、export、alias、source、pwd、exit、history |
| 外部命令(external) | 以独立可执行文件形式存在于文件系统中的程序 | 由 Shell 创建子进程执行,通过 fork() 与 exec() 加载 |
ls、cp、cat、grep、find、mount、ps、kill、chmod |
同一命令名在不同 Shell 中的实现可能存在差异。例如
local在 Bash 与 zsh 中为内置命令,用于声明局部变量;但在 dash 等严格 POSIX Shell 中不存在该命令。[[在 Bash 与 zsh 中为关键字,用于条件测试;在 dash 中同样不支持。
3.2 区分命令解析方式的方法
方法 1:type 系列(POSIX 标准内置命令)
type 为 POSIX 标准内置命令,用于解析命令来源,提供多组参数以适配不同查询需求。
无参数 type <命令>
输出命令类型的文字描述:
bash
type cd
# 输出:cd is a shell builtin
type ls
# 输出:ls is aliased to `ls --color=auto'
type echo
# 输出:echo is a shell builtin
输出结果判定规则:
- 显示
shell builtin→ 内部命令 - 显示绝对文件路径(如
/usr/bin/ls)→ 外部命令 - 显示
aliased to→ 别名 - 显示
is a function→ 自定义 Shell 函数 - 显示
is a shell keyword→ Shell 关键字
参数 -t:仅输出标准化类型标识
type -t 输出固定 5 种关键词,不含额外描述文本:
builtin:Shell 内部命令alias:命令别名function:自定义函数file:磁盘外部可执行文件keyword:Shell 关键字
bash
type -t read
# 输出 builtin,表明 read 为内部命令
alias read="echo test"
type -t read
# 输出 alias
read() { echo "自定义函数"; }
type -t read
# 输出 function
参数 -a:列出同名命令全部实现
type -a 展示同一名称下存在的别名、函数、内置命令及外部程序,输出顺序自上而下为调用优先级递减:
bash
type -a echo
# echo is a shell builtin
# echo is /usr/bin/echo
补充参数
type -p <命令>:仅输出当前优先级最高的外部可执行文件路径;若当前优先级最高的实现为别名、内置命令或函数,则无输出。type -P <命令>:强制遍历PATH环境变量检索外部可执行文件,输出首个匹配路径,忽略别名、内置命令及函数。
方法 2:command 内置工具
command -V <命令>:输出命令类型的文字描述,效果与无参type等价,可识别内部命令、别名及函数;command -v <命令>:输出简洁,可用于脚本条件判断:- 输出纯命令名 → 内部命令、别名或函数;
- 输出完整文件路径 → 外部命令。
bash
command -v cd
# 输出 cd
command -v ls
# 输出 ls --color=auto
command -v date
# 输出 /usr/bin/date
局限:无法列出同名命令的全部实现,仅展示当前优先级最高的项。
方法 3:which 外部工具
仅遍历 PATH 环境变量检索磁盘可执行文件,忽略内部命令、别名及自定义函数。
bash
which ls
# 输出:/usr/bin/ls
which cd
# 无输出
- 有路径输出 → 该名称存在外部可执行程序;
- 无输出 → 该名称仅为内部命令、别名或函数。
附加参数:which -a <命令>,列出 PATH 内全部同名外部可执行文件,输出顺序跟随 PATH 目录优先级。
方法 4:whereis 外部工具
检索二进制文件、源码及 man 手册路径,不识别 Shell 内部命令、别名及函数,仅用于查找磁盘程序文件。
bash
whereis ls
# ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz
方法 5:help 命令(Bash 特有,仅查询内部命令)
仅能查询 Bash 内置命令的帮助文档,外部命令、别名及函数无法匹配:
bash
help cd
# 输出 cd 用法说明
help ls
# 输出:no help topics match `ls'. Try `help help' or `man -k ls' or `info ls'.
- 正常打印帮助文本 → Bash 内部命令;
- 提示无匹配主题 → 外部命令、别名或函数。
外部命令的帮助文档通常通过以下方式查询:
-
man <命令>:查看系统手册页(manual page),为外部命令最权威、最完整的文档来源。bashman ls man grep -
<命令> --help:调用命令自身的帮助选项,输出简要用法说明。bashls --help grep --help -
info <命令>:查看 GNU info 格式的层级化文档,部分 GNU 项目命令提供比man更详细的说明。bashinfo coreutils info bash -
apropos <关键词>与man -k <关键词>:在手册页数据库中检索与关键词匹配的手册页条目。二者在功能上等价,均检索
whatis数据库,在手册页 NAME 节中查找匹配项:bashapropos copy man -k copy维度 aproposman -k命令性质 独立外部命令 man命令的选项参数语义 按主题关键词查找相关手册页 在手册页数据库中搜索关键词 底层机制 依赖 /var/cache/man/index.db(或/usr/share/man/whatis)数据库,由mandb或makewhatis生成与 apropos共享同一数据库额外选项 支持 -r(正则表达式)、-e(精确匹配)等通过 man的选项体系传递参数若数据库未初始化或过期,二者均返回空结果。更新命令:
bashsudo mandb
方法 6:builtin 关键字(验证内部命令可用性)
强制调用同名内部命令,若执行报错则表明该名称不属于内部命令:
bash
builtin cd --help
# 正常输出帮助,表明 cd 为内部命令
builtin testabc
# bash: builtin: testabc: 不是 shell 内嵌
方法 7:哈希表工具 hash(管理外部命令缓存)
Bash 缓存 PATH 检索到的外部命令路径至哈希表,仅作用于外部程序,不识别内部命令、别名及函数:
bash
hash
# 打印所有已缓存外部命令与对应路径
hash -t ls
# 输出缓存的 ls 二进制路径
hash -r
# 清空哈希缓存,重新遍历 PATH
3.3 同名命令的解析优先级
Shell 解析输入命令时,匹配优先级由高至低为:
- 别名(Alias)
- Shell 关键字(Keyword):
if、for、while、case等流程控制语法 - 自定义函数(Function)
- 内部命令(Builtin)
- 外部命令(External):按
PATH环境变量目录从左至右依次检索,靠前目录内程序优先级更高
注:关键字在语法分析阶段识别;别名扩展受
expand_aliases选项控制,在交互式 Bash 中默认开启,在非交互式脚本中默认关闭。
type -a <命令> 可展示该名称全部匹配项(关键字除外),输出顺序与优先级顺序对应:
bash
type -a echo
# echo is a shell builtin
# echo is /usr/bin/echo
# echo is /bin/echo
强制调用低优先级实现的方式
- 绕过别名:
\命令名、command 命令名; - 绕过函数及别名,强制使用内部命令:
builtin 命令名; - 强制调用磁盘外部程序:书写完整绝对路径
/usr/bin/echo。
3.4 批量查看当前 Shell 内部命令
Bash 环境
bash
# 输出所有内置命令名称列表
compgen -b
# 查看内置命令手册
man bash-builtins
筛选示例,校验单个名称是否为内置命令:
bash
compgen -b | grep "^cd$"
Zsh 环境
内置 whence 工具,功能与 Bash type 等价:
zsh
whence -a ls
# 按优先级输出 ls 全部实现
whence -t ls
# 仅输出类型标识
3.5 命令状态查看方案
命令状态分为四类,配套对应查询手段:
-
解析类型状态(区分内置、别名、函数及外部程序)
type -t <命令>:单行标准化类型;type -a <命令>:全部实现与优先级;command -V <命令>:文字描述。
-
哈希缓存状态(仅外部命令)
hash -t <命令>:查看缓存路径;hash -l:列出全部缓存条目。
-
磁盘文件可执行状态(仅外部命令)
bash# 判断文件是否具备可执行权限 test -x /usr/bin/ls && echo "可执行" # 查看文件权限 ls -l /usr/bin/ls -
自定义别名及函数状态
bashalias # 列出全部别名 alias ls # 查看单个别名 declare -F # 列出所有已定义函数名 declare -f 函数名 # 打印函数完整代码
3.6 内部命令与外部命令的区别
- 执行效率 :内部命令在当前进程运行,无创建子进程的资源开销;外部命令需通过
fork()与exec()创建子进程。 - 环境变量作用域 :
cd、export等内部命令直接修改当前 Shell 的运行环境;外部命令运行在独立子进程,修改的环境变量仅在子进程内生效,无法传递至父 Shell。 - 脚本兼容性:不同 Shell 内置命令集合存在差异,跨 Shell 脚本开发需注意兼容性问题。
3.7 BusyBox 嵌入式环境
BusyBox 系统的命令归属逻辑与标准 Linux 发行版存在区别,需结合文件系统路径综合判断:
- BusyBox Applet :
/bin/ls、/bin/cat等均为指向busybox的软链接,type会判定为外部命令,但实际由单一 multi-call 二进制调度,不加载独立可执行文件; - Ash 内置命令 :BusyBox 默认
ash交互 Shell 实现cd、pwd、export等 POSIX 标准内置命令,执行时不经过 BusyBox Applet; - 混合执行逻辑 :输入
echo优先调用 ash 内部实现;书写完整路径/bin/echo则触发 BusyBox 的 echo Applet。
在 BusyBox 环境中,不可仅凭 type 输出判定执行模型,需结合文件链接指向区分普通独立外部程序与 BusyBox 多调用二进制程序。
3.8 工具能力对比
| 工具 | 识别别名 | 识别内置命令 | 识别自定义函数 | 列出同名全部实现 | 体现调用优先级 | 仅检索磁盘文件 |
|---|---|---|---|---|---|---|
type -a |
✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
type -t |
✅ | ✅ | ✅ | ❌ | ✅(仅最高优先级) | ❌ |
command -V |
✅ | ✅ | ✅ | ❌ | ✅(仅最高优先级) | ❌ |
which -a |
❌ | ❌ | ❌ | ✅(仅外部文件) | ✅(PATH 顺序) | ✅ |
whereis |
❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
hash |
❌ | ❌ | ❌ | ❌ | ✅(缓存) | ✅ |
whence(zsh) |
✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
help |
❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
4. 功能范围
BusyBox 不仅包含常规文件操作与文本处理工具,还提供系统级服务:
- init 进程 :支持作为 PID 1 启动,解析
/etc/inittab,管理系统初始化与进程回收; - Shell:内置 ash(Almquist Shell)与 hush,兼容 POSIX 与部分 Bash 扩展;
- 网络服务 :
httpd(简易 Web 服务器)、telnetd(远程登录)、udhcpd/udhcpc(DHCP 服务端/客户端)、ntpd(时间同步)、ftpd(文件传输)等; - 文件系统工具 :
mount、umount、fdisk、mkfs系列等; - 编辑器 :
vi、awk、sed等。
需要指出的是,BusyBox 的各 applet 通常为精简实现,功能选项较 GNU 版本有所缩减。例如,grep 不支持 -P(Perl 正则),tar 对某些归档格式的支持有限。若需完整功能,可通过包管理器安装 GNU Coreutils 进行替换。
5. 体积优势与资源占用
BusyBox 的设计目标之一是在功能完备的前提下尽可能压缩体积。典型数据如下:
| 配置方案 | 体积(静态链接,musl libc) |
|---|---|
| 最小配置(约 20 个基础命令) | ~350 KB |
| 全功能默认配置(1.38.0,静态链接 musl) | ~1.0 MB(text: 1,037,438 B; data: 555 B; bss: 4,832 B) |
| GNU Coreutils + Bash | >10 MB(动态链接场景下仅二进制即达数十 MB,加上依赖库可达 100 MB 以上) |
对于仅配备 8 MB NOR Flash 的嵌入式板卡,采用 BusyBox 可在容纳完整命令行环境的同时,为应用程序保留存储空间。
6. 无 MMU 架构支持
BusyBox 可运行于无内存管理单元(MMU)的处理器架构上,如 ARM7、ColdFire 等。配合 uClinux(专为无 MMU 系统设计的 Linux 移植版本),BusyBox 提供了完整的命令行环境。这一特性使其成为低成本嵌入式设备的默认选择。
7. 项目历史与维护者
| 时间 | 维护者 | 主要贡献 |
|---|---|---|
| 1996 | Bruce Perens | 创建 BusyBox,初衷是为 Debian 安装软盘提供可引导的急救环境 |
| 1996--1998 | Enrique Zanardi | 维护 Debian 启动软盘相关功能 |
| 1998--1999 | Dave Cinege | 模块化改造,推动 BusyBox 向嵌入式领域发展 |
| 1999--2006 | Erik Andersen | 大幅扩展 applet 数量;同时发起 uClibc 项目 |
| 2006 前后 | Rob Landley | 短暂维护,后因许可证争议离开并创立 Toybox |
| 2006--至今 | Denys Vlasenko | 现任维护者,持续推动代码精简与功能更新 |
截至 2026 年 8 月,BusyBox 最新版本为 1.38.0 (unstable),于 2026 年 5 月 13 日发布;当前最新的稳定版本为 1.36.1(2023 年 5 月 19 日),1.37.x 为开发分支。
8. 同类工具与替代方案
除 BusyBox 外,嵌入式与极简系统领域还存在若干功能相似的 Unix 工具集:
8.1 Toybox
由 Rob Landley 发起,采用 0BSD 许可证(较 GPLv2 更为宽松)。Toybox 强调 POSIX-2008 与 LSB 4.1 标准兼容性,代码结构更为清晰。自 Android 6.0(Marshmallow)起,Google 将 Toybox 并入 AOSP,作为 Android 用户空间的基础命令集,替代了此前基于 GPL 的 BusyBox。Toybox 同样支持 make menuconfig 裁剪,并可通过 make root 构建最小可启动 Linux 系统。
8.2 sbase / ubase
由 suckless.org 社区开发,遵循 POSIX 标准,追求代码简洁与可移植性。sbase 提供跨 Unix 平台的可移植工具,ubase 则专注于 Linux 特有功能。二者可组合构建类 BusyBox 的基础系统,亦支持编译为单一二进制(sbase-box)并通过符号链接调用。
8.3 embutils
由 Felix von Leitner(fefe)开发,采用 GPLv2 许可证,专为嵌入式场景设计,体积较 BusyBox 更为精简,但 applet 覆盖范围相对有限。
8.4 GNU Coreutils
传统 Linux 发行版(如 Debian、Fedora)的标准工具集,功能最为完整,但体积庞大,不适合资源受限环境。在 Alpine Linux 等系统中,可通过 apk add coreutils 选择性安装,以补充 BusyBox 未覆盖的高级功能。
8.5 对比总结
| 项目 | 许可证 | 特点 | 典型应用场景 |
|---|---|---|---|
| BusyBox | GPLv2 | 功能全面,社区成熟,历史悠久 | 嵌入式 Linux、路由器、IoT 设备 |
| Toybox | 0BSD | 标准兼容,代码清晰,无 GPL 传染性 | Android、商业闭源嵌入式产品 |
| sbase/ubase | MIT/X11 | 极简代码,suckless 哲学 | 自定义极简系统、教学研究 |
| embutils | GPLv2 | 体积极小,功能精简 | 超受限嵌入式环境 |
| GNU Coreutils | GPLv3 | 功能最完整,文档齐全 | 桌面/服务器 Linux 发行版 |
9. 许可证与法律诉讼
BusyBox 采用 GPLv2 许可证。2007 年 9 月,软件自由法律中心(SFLC)代表开发者 Erik Andersen 与 Rob Landley,对美国 Monsoon Multimedia 公司提起诉讼,指控其在 HAVA 电视转播设备中使用 BusyBox 但未按 GPLv2 要求提供源代码。该案于 2007 年 10 月和解,成为美国历史上首起基于 GPL 的版权侵权诉讼。
此后,SFLC 及软件自由保护协会(SFC)陆续对 Xterasys、High-Gain Antennas、Verizon(Actiontec 路由器)、Best Buy、JVC、Samsung、Westinghouse 等多家公司提起类似诉讼。这些案件确立了 GPL 在美国法院的可执行性,推动了嵌入式行业的开源合规意识。
值得注意的是,Rob Landley 后来公开反思诉讼策略,认为其未为 BusyBox 带来实质性代码贡献,反而促使部分厂商转向非 Linux 平台。这一背景直接推动了 Toybox 的诞生。
10. 安全与合规
BusyBox 作为广泛部署的基础组件,其安全性直接影响大量嵌入式与 IoT 设备。近年披露的典型漏洞包括:
- CVE-2021-423xx 系列:Claroty 与 JFrog 研究人员发现 14 个漏洞,涉及拒绝服务(DoS)、信息泄露与远程代码执行(RCE),于 1.34.0 版本修复;
- CVE-2025-46394 :
tarapplet 未能正确处理终端转义序列,导致文件名可能被隐藏,影响版本至 1.37.0,于 1.38.0 修复; - CVE-2025-60876 :
wgetapplet 存在 CRLF 注入与 HTTP 请求拆分漏洞,影响版本 ≤1.37.0,于 1.38.0 通过增加 URL 控制字符校验修复。
对于基于 BusyBox 构建商业产品的厂商,需严格遵守 GPLv2 开源义务,提供对应版本的完整源代码,以规避法律风险。
11. Linux 系统基础命令
以下命令符合 POSIX 标准,在几乎所有 Linux 发行版(以及类 Unix 系统)中均可用:
11.1 常见内部命令(Builtin)
这些命令由 Shell 直接提供,不依赖外部二进制文件:
| 命令 | 功能 |
|---|---|
cd |
切换当前工作目录 |
echo |
输出文本(部分 Shell 中为外部命令) |
pwd |
打印当前工作目录 |
export |
设置或显示环境变量 |
unset |
删除变量或函数 |
alias / unalias |
设置 / 删除命令别名 |
source / . |
在当前 Shell 中执行脚本 |
exit |
退出当前 Shell |
history |
显示命令历史 |
jobs |
显示后台任务列表 |
fg / bg |
将任务调至前台 / 后台 |
kill |
发送信号给进程(Bash 中为内部命令) |
test / [ |
条件判断 |
shift |
移动位置参数 |
return |
从函数返回 |
break / continue |
循环控制 |
eval |
执行参数作为命令 |
exec |
用新程序替换当前进程 |
trap |
设置信号处理程序 |
umask |
设置文件创建掩码 |
wait |
等待后台进程结束 |
times |
显示进程累计时间 |
完整列表可通过
help或man bash-builtins查看。
11.2 常见外部命令
这些命令以独立可执行文件形式存在于 /bin、/usr/bin、/sbin、/usr/sbin 等目录:
| 命令 | 功能 | 典型路径 |
|---|---|---|
ls |
列出目录内容 | /usr/bin/ls |
cp |
复制文件或目录 | /usr/bin/cp |
mv |
移动或重命名文件 | /usr/bin/mv |
rm |
删除文件或目录 | /usr/bin/rm |
cat |
连接并显示文件内容 | /usr/bin/cat |
grep |
文本模式搜索 | /usr/bin/grep |
find |
文件系统搜索 | /usr/bin/find |
chmod |
修改文件权限 | /usr/bin/chmod |
chown |
修改文件所有者 | /usr/bin/chown |
ps |
查看进程状态 | /usr/bin/ps |
top |
动态进程监控 | /usr/bin/top |
df |
查看磁盘空间 | /usr/bin/df |
du |
查看目录/文件大小 | /usr/bin/du |
mount |
挂载文件系统 | /usr/bin/mount |
umount |
卸载文件系统 | /usr/bin/umount |
tar |
归档工具 | /usr/bin/tar |
awk |
文本处理语言 | /usr/bin/awk |
sed |
流编辑器 | /usr/bin/sed |
sort |
文本排序 | /usr/bin/sort |
wc |
统计行数、字数、字节数 | /usr/bin/wc |
head / tail |
查看文件首尾 | /usr/bin/head |
ln |
创建链接 | /usr/bin/ln |
mkdir / rmdir |
创建 / 删除目录 | /usr/bin/mkdir |
touch |
创建空文件或更新时间戳 | /usr/bin/touch |
date |
显示或设置系统时间 | /usr/bin/date |
whoami |
显示当前用户名 | /usr/bin/whoami |
uname |
显示系统信息 | /usr/bin/uname |
env |
显示或修改环境变量 | /usr/bin/env |
id |
显示用户/组 ID | /usr/bin/id |
kill(外部) |
发送信号(部分 Shell 中) | /usr/bin/kill |
12. 结语
BusyBox 自 1996 年诞生以来,从一张 1.44 MB 软盘上的安装工具,发展为全球路由器、机顶盒、工控设备与容器镜像中广泛部署的基础设施。其通过单一二进制整合数百个 Unix 工具的设计哲学,在嵌入式资源受限场景下具有特定优势。理解内部命令与外部命令的区分方法,对于在 BusyBox 环境中进行系统调试与脚本开发具有实际意义。与此同时,Toybox 等替代项目的兴起,也为不同许可证需求与标准兼容性要求的场景提供了多元化选择。
参考资料
- BusyBox 官方网站:https://busybox.net/
- Toybox 官方网站:http://landley.net/toybox/
- sbase 项目:https://git.suckless.org/sbase/
- OpenEmbedded BusyBox 版本追踪:http://layers.openembedded.org/rrs/recipedetail/OE-Core/49/
- BusyBox - 人人都該備一個在身邊的執行檔 --- wdv4758h-notes latest documentation
https://wdv4758h.github.io/notes/cli-tui/busybox.html - 【Linux技术】BusyBox详解 - Leo.cheng - 博客园
https://www.cnblogs.com/lcw/p/3159388.html