前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BusyBox 72 变「建议收藏」

BusyBox 72 变「建议收藏」

作者头像
Java架构师必看
发布2022-08-14 10:29:17
4650
发布2022-08-14 10:29:17
举报
文章被收录于专栏:Java架构师必看

大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说BusyBox 72 变「建议收藏」,希望能够帮助大家进步!!!

BusyBox 简介

BusyBox 是一个遵循 GPL 协议、以自由软件形式发行的应用程序。Busybox 在单一的可执行文件中提供了精简的 Unix 工具集,可运行于多款 POSIX 环境的操作系统,例如 Linux(包括Android)、Hurd、FreeBSD 等等。

BusyBox 之于嵌入式

由于 BusyBox 可执行文件比较小,使得它非常适用于嵌入式系统。作者将 BusyBox 称为“嵌入式 Linux 的瑞士军刀”。

BusyBox 最初是由布鲁斯·斐伦斯在 1996 年为 Debian GNU/Linux 的软盘安装编写的,其原始构想是希望在一张软盘上能放入一个引导系统,以作为急救盘和安装盘。后来它变成了嵌入式 Linux 设备和系统和 Linux 发行版安装程序的实质标准,因为每个 Linux 可执行文件需要数 KB 的空间,而集成两百多个程序的 BusyBox 可以节省大量空间。例如:Debian GNU/Linux 的安装光盘以 BusyBox 作为Shell 程序。

BusyBox 编译安装

我们可以从 BusyBox 官网下载源码,编译安装也很简单,就是 make && make install。

安装时默认会新建一个 _install 目录,然后安装进此目录。

代码语言:javascript
复制
liyongjun@Box:~/project/c/busybox-1.32.0$ ls _install/bin/ -l
总用量 1048
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 arch -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 ash -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 base64 -> busybox
-rwxr-xr-x 1 liyongjun liyongjun 1071816 10月 12 21:53 busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 cat -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 chattr -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 chgrp -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 chmod -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 chown -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 conspy -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 cp -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 cpio -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 cttyhack -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 date -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 dd -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 df -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 dmesg -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 dnsdomainname -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 dumpkmap -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 echo -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 ed -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 egrep -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 false -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 fatattr -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 fdflush -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 fgrep -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 fsync -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 getopt -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 grep -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 gunzip -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 gzip -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 hostname -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 hush -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 ionice -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 iostat -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 ipcalc -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 kbd_mode -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 kill -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 link -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 linux32 -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 linux64 -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 ln -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 login -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 ls -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 lsattr -> busybox
lrwxrwxrwx 1 liyongjun liyongjun       7 10月 12 21:53 lzop -> busybox
...

只听到从架构师办公室传来架构君的声音:

莫买沃洲山,时人已知处。有谁来对上联或下联?

BusyBox 72 变

今天讲的重点是,为什么将 busybox 软链接成 ls,运行后就是 ls 的功能;软链接成 cat,运行后就是 cat 的功能?

我们先看看运行 busybox 有几种方式

(1)busybox <命令名>,如

代码语言:javascript
复制
此代码由Java架构师必看网-架构君整理
busybox ls
busybox cat

(2)cp busybox <命令名>

代码语言:javascript
复制
cp busybox ls
ls

(3)ls -n busybox <命令名>

代码语言:javascript
复制
此代码由Java架构师必看网-架构君整理
ln -n busybox ls
ls

而这三种方式最根本的差异是传递给 busybox 的 argc argv 参数不同。

(1)busybox ls:argc = 2, argv0 = “busybox”, argv1 = “ls”

(2)ls:argc = 1,argv0 = “ls”

(3)ls:argc = 1,argv0 = “ls”

然后我们看下 busybox 是如何处理 argc argv 的。

busybox 的入口是 appletlib.c 中的 main 函数,main 函数最终调用 run_applet_and_exit()

代码语言:javascript
复制
int main(int argc UNUSED_PARAM, char **argv)
{ 
   
...
	run_applet_and_exit(applet_name, argv);
}

我们看下 run_applet_no_and_exit() 的实现

代码语言:javascript
复制
static NORETURN void run_applet_and_exit(const char *name, char **argv)
{ 
   
	if (is_prefixed_with(name, "busybox")){ 
   
		exit(busybox_main(/*unused:*/ 0, argv));
	}

	/* find_applet_by_name() search is more expensive, so goes second */
	{ 
   
		int applet = find_applet_by_name(name);
		if (applet >= 0)
			run_applet_no_and_exit(applet, name, argv);
	}

	exit(127);
}

其中 is_prefixed_with() 函数就是用来判断第一个参数是否为 busybox,如果是就执行 busybox_main(/*unused:*/ 0, argv),而在 busybox_main() 中会移除第一个参数 busybox,然后再次调用 run_applet_and_exit(),如此反复,直到第一个参数不为 busybox,然后通过 find_applet_by_name() 找到对应功能的 main 函数去执行。

举个实际的例子说明一下,例如我们运行 busybox busybox ls,源码中函数调用关系如下:

代码语言:javascript
复制
1) main()					// argv[0] = "busybox"; argv[1] = "busybox"; argv[2] = "ls"; 
2) run_applet_and_exit()	// 判断到第一个参数为 busybox, 则调用 busybox_main()
3) busybox_main()			// 去除第一层 busybox,传递给下面 busybox ls
4) run_applet_and_exit()	// 判断到第一个参数为 busybox, 则调用 busybox_main()
5) busybox_main()			// 去除第二层 busybox,传递给下面 ls
6) run_applet_and_exit()	// 判断到第一个参数不为 busybox, 则执行 find_applet_by_name()
7) find_applet_by_name()	// 找到 ls 对应的功能入口函数为 ls_main()
8) run_applet_no_and_exit()	// 执行 ls_main() 然后直接退出

可以看出,前面有多少个 busybox, 就会执行多少次 3) 4) 步骤循环,直到去除前面所有的 busybox,露出真正的命令,然后去执行命令。这就是为什么我们以多种方式运行 busybox 命令,最终都能成功找到命令并运行的原因。

几种执行方式对比

  1. 先拷贝或软连接,然后执行 cp busybox ls ls -n busybox ls ls
代码语言:javascript
复制
main()
	run_applet_and_exit()
		run_applet_no_and_exit()
			ls_main()
  1. busybox 加参数形式执行 busybox ls
代码语言:javascript
复制
main()
	run_applet_and_exit()
	
		busybox_main()	// 剥掉一层 busybox
			run_applet_and_exit()
			
				run_applet_no_and_exit()
					ls_main()
  1. busybox busybox ls
代码语言:javascript
复制
main()
	run_applet_and_exit()
	
		busybox_main()	// 剥掉一层 busybox
			run_applet_and_exit()
			
				busybox_main()	// 再剥掉一层 busybox
					run_applet_and_exit()
					
						run_applet_no_and_exit()
							ls_main()

小结

以不同方式运行 busybox 会导致传递给 busybox 的 argv 参数不同,busybox ls 这种形式递给 busybox 的参数是 busyboxls; 而将 busybox 拷贝成 ls,或软连接成 ls 后,再执行 ls,则传递给 busybox 的参数就只有 ls。而 busybox 又有能力去除前面多余的 busybox,所以使得我们使用多种形式执行命令时,busybox 能够自动地执行相应命令的功能。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-102,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • BusyBox 简介
  • BusyBox 之于嵌入式
  • BusyBox 编译安装
  • BusyBox 72 变
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档