一 busybox的作用
- busybox的作用:本质是一个应用程序,它实现了响应shell发送的ls/cp等各种指令。
shell发送ls指令==执行busybox ls指令
验证上述结论:
根目录系统bin文件夹:/bin存放二进制可执行文件(ls,cat,mkdir等),常用命令一般都在这里。
上图可以看出:ls指令实际上链接到了busybox文件
- busybox的作用:linux内核上电执行的初始进程/sbin/init也是busybox来实现的。
二 busybox源码分析1
注:以cp命令和init命令为例说明
- cp指令对应cp.c文件,里面有cp_main()子函数
- init程序:init.c文件如下图。当linux内核调用sbin/init的时候就执行init_main程序。
三 busybox源码分析2-init程序分析
init程序的作用:最终目的启动用户程序
设计思路:
(1)读取配置文件
(2)解析配置文件
(3)根据配置文件执行用户程序
init程序的源码分析:
busybox-> init_main
parse_inittab();
file = fopen(INITTAB, "r"); //(1)打开配置文件etc/inittab
//(2)解析文件过程;
new_init_action(a->action, command, id);//(3)根据配置文件执行用户程序
//a->action:inittab配置文件<action>
//command:inittab配置文件<process>
//id:inittab配置文件格式<id>
run_actions(SYSINIT);
waitfor(a, 0);//执行应用程序,等待它执行完毕
delete_init_action(a);把SYSINIT对应的应用程序从链表中删除,即只允许执行一次
run_actions(WAIT);
run_actions(ONCE);
while(1){
...
}
inittab配置文件格式:
<id>:<runlevels>:<action>:<process>
<id>=> /dev/id,用作终端:stdin,stdout,stderr:printf,scanf,err
<runlevels>:=>忽略
<action>:执行时机
有效取值: sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.
<process>:应用程序或者脚本
- 按键信号处理:例如按住ctrl+alt+del按键的时候会进行特定处理
- linux调用本程序的时候无参数输出,所以argc=1执行
parse_inittab();
四 busybox源码分析3-分析函数parse_inittab()
- 进入
parse_inittab();
函数进行分析:
(1)打开配置文件INITTAB
INITTAB文件存放地址参见下图。
INITTAB配置文件格式:(参见busybox-1.7.0\examples\inittab)
# <id>:<runlevels>:<action>:<process>
INITTAB配置文件包含的信息:指定哪个用户程序;程序何时执行;
(2)如果没有默认的配置文件会进行下面若干处理
(3)解析INITTAB文件:
a.如果出现#字符或者回车字符直接忽略整行
b.把id字符串前面加上/dev/
,最终执行LINE846行代码:进入new_init_action();
函数
- 分析
new_init_action(int action, const char *command, const char *cons);
函数:
以new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
即new_init_action(0x004, "-/bin/sh", "/dev/vc/2");
为例分析
下表是重点!!!!!!
例子中传入的参数 参数含义 函数声明原型
ASKFIRST:0x004------------------------>inittab配置文件<action>调用时机------------>int action
bb_default_login_shell:"-/bin/sh"----->inittab配置文件<process>应用程序或者脚本---->const char *command
VC_2:"/dev/tty2"---------------------->inittab配置文件格式<id>用作终端------------->const char *cons
设计思路:创建一个init_action结构,用传入的参数进行初始化;把该结构放入init_action_list链表
里面init_action
结构体定义如下图:
struct init_action *next;为链表用。
action : 执行时机
pid : 进程号
command : 对应执行程序 (-/bin/sh)
terminnal : 对应终端(/dev/tty2)
首先,分析代码LINE705~LINE716该函数遍历链表所有数据,检查链表中是否存在某个数据和和传进来的 action,*command,*cons 具有一样的参数。如果有就直接返回;
初次进入本函数,该链表为空,所以该条件不成立。
其次,分析代码LINE718~LINE730行,
LINE718:给结构体指针new_action分配了一个空间和位置;
LINE719~LINE723:如果init_action_list链表为空,则直接放置在首地址上,如果非空则放置在最末尾
LINE724~LINE726:把本次输入的参数存储进入结构体
- 根据上一节分析的结论,
new_init_action()
函数会向链表中添加传入的参数。那么,如果需要传递一些默认的配置参数就可以通过反复调用本函数完成。 - 回到函数
parse_inittab();
的代码:如果存在默认的配置文件会进行解析后完成后续操作,如果不存在默认的配置文件,代码也会提供默认配置,如下图代码所示:
- 根据以上代码反推出默认的配置文件
根据new_init_action()函数原型和init_tab
说明文件:
'action’有效取值: sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.
inittab配置文件格式:<id>:<runlevels>:<action>:<process>
配置文件默认取值:
::CTRLALTDEL:reboot
::SHUTDOWN:umount -a -r
::RESTART:init
::ASKFIRST:-/bin/sh
/dev/tty2::ASKFIRST:-/bin/sh
/dev/tty3::ASKFIRST:-/bin/sh
/dev/tty4::ASKFIRST:-/bin/sh
::SYSINIT:/etc/init.d/rcS
五 busybox源码分析4-继续init程序分析
- 剩余的步骤简写如下:
run_actions(SYSINIT);
run_actions(WAIT);
run_actions(ONCE);
while (1)
{
run_actions(RESPAWN);
if (a->pid == 0)
a->pid = run(a);
run_actions(ASKFIRST);
if (a->pid == 0)
a->pid = run(a);
打印Please press Enter to activate this console.
等待回车按键
创建进程
wpid = wait(NULL); //等待以上两个子进程退出
while (wpid > 0)
a->pid = 0; //哪个子进程退出,就设置其pid=0,然后继续执行
}
- 分析函数
run_actions(SYSINIT);/run_actions(WAIT);
LINE533:init_action_list链表是刚刚new_init_action函数添加了若干个默认初始值的链表。此时该链表已经有了初始化值;
LINE535:遍历链表中的每个值,若与传入的参数”action“一致则执行下面操作
LINE539~LINE541:
首先传入的参数是SYSINIT,则执行
LINE540waitfor(a, 0);//执行应用程序,等待它执行完毕;
delete_init_action(a);把SYSINIT对应的应用程序从链表中删除,即只允许执行一次
//----------------------------------------------------------------------------------------
插叙
分析:函数waitfor()
LINE513:执行应用程序a run(a)
—创建process子进程
LINE514~LINE524:等待子进程执行结束
//----------------------------------------------------------------------------------------//
- 分析函数
run_actions(ONCE);
直接执行
LINE543:run(a); 不等待执行完毕
LINE544:delete_init_action(a);
4. 分析函数run_actions(RESPAWN);run_actions(ASKFIRST);
参考上图,直接执行LINE548~LINE549:只有进程号pid为0的时候才会执行
/* Only run stuff with pid==0. If they have* a pid, that means it is still running */
if (a->pid == 0)
a->pid = run(a);
- 那么
RESPAWN
和ASKFIRST
有什么区别呢?需要分析run()
函数
LINE464:如果是ASKFIRST那么打印的信息
LINE478:等待控制台输入回车,否则会在这里死循环
LINE500:创建子进程
六 busybox源码分析4-ctrlaltdel, and shutdown.
以上我们知道action允许值的sysinit, respawn, askfirst, wait, once,restart
均在源码中有所反应,还有ctrlaltdel, shutdown
没有出现在代码中。
action的合理值
action: Valid actions include: sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, shutdown.
-
回到
init_main
函数,如下图所示。
当键盘按下“crtl+alt+del”的时候,会给内核发送SIGINT
信号;
内核代码init_main
接收到SIGINT
信号时执行ctrlaltdel_signal
函数。
-
分析
ctrlaltdel_signal
函数
进入run_actions(CTRLALTDEL)
函数,该函数上面有分析。
所以,当键盘按下“crtl+alt+del”的时候,内核执行CTRLALTDEL
程序。
对于其他的信号,内核会在1.signal
的程序对应执行相关代码。
七 最小的根文件系统需要的项(笔记4.1 4.2小结)
(init 进程需要)
- 打开终端: /dev/console, /dev/NULL
不设置 inittab 格式中的 id(标准输入、输出和标准错误)时,就定位到 /dev/NULL 中去。 - /sbin/init(init 程序):它本身就是 busybox .
- /etc/inittab:需要配置文件
配置文件若指定了某些应用程序或执行脚本–这些必须存在,不存在会有默认的。 - 应用程序需要的库(printf,fopen、 fwrite 等函数需要库函数) 。