推荐一个零声学院免费教程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
目录
书接上文,前一节已经成功构建了一个最简单的根文件系统,并且在根文件系统中运行了一个静态编译的C程序。本文编写一个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命令。
- / # which ls
- /bin/ls
- / # rm /bin/ls
- / # ls
- -/bin/sh: ls: not found
- / # ls
现在,ls命令已经被删除,且执行时-/bin/sh: ls: not found,说明,并没有去直接执行busybox,这个还是很关键的。这说明即便是busybox自带的命令也要受PATH环境变量的约束。
如下所示,先验证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
- / #
-
这个自己做出来以后才发现,真相原理一直就在眼前,只是我们一直把它神话了。
代码如下所示:保存命名为main.c
- #include
-
- 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。
首先我创建了一个指向busy的链接,命名为lk,
lkmao@ubuntu:~$ ln -s busy lk
然后执行lk,见证结果啦
- lkmao@ubuntu:~$ ./lk
- argv[0] = ./lk .
看到了吧。基本原理就是这样了。
修改main.c文件,并重命名为busy.c的代码如下所示:
- #include
- #include
-
- #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
- lkmao@ubuntu:~$ ./lk
- busy.c:main:11 - argv[0] = ./lk .
- busy.c:main:19 - i'm comand lk
- lkmao@ubuntu:~$
- lkmao@ubuntu:~$ ./hg
- busy.c:main:11 - argv[0] = ./hg .
- busy.c:main:24 - i'm comand hg
- lkmao@ubuntu:~$
好了,执行成功。
创建文件夹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神话了,神话到不敢去探究它的真面目,发现真相的那一刻我惊呆了,这感觉,有点酸爽啊。