BusyBox 72 变

Posted Li-Yongjun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 目录,然后安装进此目录。

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

BusyBox 72 变

今天讲的重点是,为什么将 busybox 软链接成 ls,运行后就是 ls 的功能;软链接成 cat,运行后就是 cat 的功能?
我们先看看运行 busybox 有几种方式
(1)busybox <命令名>,如

busybox ls
busybox cat

(2)cp busybox <命令名>

cp busybox ls
ls

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

ln -n busybox ls
ls

而这三种方式最根本的差异是传递给 busybox 的 argc argv 参数不同。
(1)busybox ls:argc = 2, argv[0] = “busybox”, argv[1] = “ls”
(2)ls:argc = 1,argv[0] = “ls”
(3)ls:argc = 1,argv[0] = “ls”
然后我们看下 busybox 是如何处理 argc argv 的。
busybox 的入口是 appletlib.c 中的 main 函数,main 函数最终调用 run_applet_and_exit()

int main(int argc UNUSED_PARAM, char **argv)
{
...
	run_applet_and_exit(applet_name, argv);
}

我们看下 run_applet_no_and_exit() 的实现

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,源码中函数调用关系如下:

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
main()
	run_applet_and_exit()
		run_applet_no_and_exit()
			ls_main()
  1. busybox 加参数形式执行
    busybox ls
main()
	run_applet_and_exit()
	
		busybox_main()	// 剥掉一层 busybox
			run_applet_and_exit()
			
				run_applet_no_and_exit()
					ls_main()
  1. busybox busybox ls
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 能够自动地执行相应命令的功能。

以上是关于BusyBox 72 变的主要内容,如果未能解决你的问题,请参考以下文章

CSS3也会72变

1165: 零起点学算法72——首字母变大写

看我72变,阿里HBase数据压缩编码探索

一个标签的72变,打造一个纯CSS图标库

从活动中更改片段的变量值

在 TabLayout 中的片段之间滑动变慢