Busybox实践2:分析busybox文件链接原理并编程模拟实现自己的busybox文件
https://ke.qq.com/course/417774?flowToken=1010783
目录
前言
一 busybox文件原理
基本原理
验证一下
验证1 删掉ls命令:
验证2 自己创建一个ls的链接:
二 用代码来证明原理
1 先写一个普通的小程序
2 创建链接,见证奇迹
三 模拟一个busybox
1 编写模拟busybox程序代码
2 运行验证
1) 验证lk命令
2)验证hg命令
四 将我的命令lk和hg添加到PATH环境变量
添加PATH环境变量
开始验证了
总结
前言
书接上文,前一节已经成功构建了一个最简单的根文件系统,并且在根文件系统中运行了一个静态编译的C程序。本文编写一个busybox模拟程序,来说明busybox文件的实现原理。
一 busybox文件原理
为什么busybox一个文件可以链接成为那么多命令呢
基本原理
根据查看BusyBox源码,发现里面有很多xxx_main函数,BusyBox根文件系统运行时,输入的命令,如ls,是以argv[0]的形式,传递给BusyBox的终端交互程序,也就是说,我们输入的这个地方本身就是一个程序,如下所示,它处理我们输入的数据,例如我们输入ls,它就处理ls。
Please press Enter to activate this console.
/ #
如何找的呢,分两步:
1)它先去PATH对应的环境变量找,找到ls
2)发现ls是被链接到busybox文件,然后将ls名字传递给busybox文件,busybox文件会接收ls这个参数,并调用相应的ls_main函数,就顺利执行了ls命令。
验证一下
验证1 删掉ls命令:
/ # which ls
/bin/ls
/ # rm /bin/ls
/ # ls
-/bin/sh: ls: not found
/ # ls
现在,ls命令已经被删除,且执行时-/bin/sh: ls: not found,说明,并没有去直接执行busybox,这个还是很关键的。这说明即便是busybox自带的命令也要受PATH环境变量的约束。
验证2 自己创建一个ls的链接:
如下所示,先验证ls确实不存在,然后创建一个指向busybox的链接,并命名为ls,然后进入根目录,再次测试ls命令,结果证明,ls确实是指向busybox的链接,并且仅仅是一个普通的链接,没有没有什么特别之处,特别的是busybox对于命令行参数的处理,有机会我们也写一个自己的busybox小程序试试。
/ # cd bin/
/bin # ls
-/bin/sh: ls: not found
/bin # ln -s busybox ls
/ # ls
app bin etc linuxrc sbin test.sh usr
arm_app dev hello.c proc sys tmp var
/ #
二 用代码来证明原理
这个自己做出来以后才发现,真相原理一直就在眼前,只是我们一直把它神话了。
1 先写一个普通的小程序
代码如下所示:保存命名为main.c
#include <stdio.h>
int main(int argc,char *argv[])
{
int i = 0;
for(i = 0;i < argc;i++){
printf("argv[%d] = %s .\n",i,argv[i]);
}
return 0;
}
编译测试
lkmao@ubuntu:~$ gcc main.c -o busy
lkmao@ubuntu:~$ ./busy
argv[0] = ./busy .
看输出结果,argv[0]是程序名,是busy。
2 创建链接,见证奇迹
首先我创建了一个指向busy的链接,命名为lk,
lkmao@ubuntu:~$ ln -s busy lk
然后执行lk,见证结果啦
lkmao@ubuntu:~$ ./lk
argv[0] = ./lk .
看到了吧。基本原理就是这样了。
三 模拟一个busybox
1 编写模拟busybox程序代码
修改main.c文件,并重命名为busy.c的代码如下所示:
#include <stdio.h>
#include <string.h>
#define debug(format,...) printf("%s:%s:%d - "format"\n",\
__FILE__,__func__,__LINE__,##__VA_ARGS__);
int main(int argc,char *argv[])
{
int i = 0;
char *cmd;
for(i = 0;i < argc;i++){
debug("argv[%d] = %s .",i,argv[i]);
}
if(argv[0][0] == '.' && argv[0][1] == '/'){
cmd = &argv[0][2];
}else{
cmd = &argv[0][0];
}
if(strcmp("lk",cmd) == 0){
debug("i'm comand lk");
return 0;
}
if(strcmp("hg",cmd) == 0){
debug("i'm comand hg");
return 0;
}
debug("unknown cmd %s",cmd);
debug("do you want \"lk\" or \"hg\"");
return 0;
}
编译,然后创建相应的链接文件lk和hg。
lkmao@ubuntu:~$ gcc busy.c -o busy
lkmao@ubuntu:~$ ln -s busy lk
lkmao@ubuntu:~$ ln -s busy hg
2 运行验证
1) 验证lk命令
lkmao@ubuntu:~$ ./lk
busy.c:main:11 - argv[0] = ./lk .
busy.c:main:19 - i'm comand lk
lkmao@ubuntu:~$
2)验证hg命令
lkmao@ubuntu:~$ ./hg
busy.c:main:11 - argv[0] = ./hg .
busy.c:main:24 - i'm comand hg
lkmao@ubuntu:~$
好了,执行成功。
四 将我的命令lk和hg添加到PATH环境变量
添加PATH环境变量
创建文件夹mycmd,将文件busy lk hg都放进去
lkmao@ubuntu:~$ mkdir mycmd
lkmao@ubuntu:~$ mv busy lk hg mycmd/
lkmao@ubuntu:~$
然后通过export命令将mycmd放到PATH环境变量中
lkmao@ubuntu:~$ export PATH=${PATH}:/home/lkmao/mycmd
lkmao@ubuntu:~$ echo $PATH
/home/lkmao/bin:/home/lkmao/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin:/home/lkmao/mycmd
lkmao@ubuntu:~$
从PATH的值可知,我们已经将/home/lkmao/mycmd添加到了PATH的尾部。
开始验证了
验证lk
lkmao@ubuntu:~$ lk
busy.c:main:11 - argv[0] = lk .
busy.c:main:19 - i'm comand lk
验证hg
lkmao@ubuntu:~$ hg
busy.c:main:11 - argv[0] = hg .
busy.c:main:24 - i'm comand hg
验证完毕,结果OK.
总结
其实这个原理并不复杂,甚至也可以说简单,只是我们把busybox神话了,神话到不敢去探究它的真面目,发现真相的那一刻我惊呆了,这感觉,有点酸爽啊。