1:信息
busybox版本:busybox-1.7.0
2:关于busybox
busybox是一个集成了一百多个最常用linux命令和工具的软件,他甚至还集成了一个http服务器和一个telnet服务器,而所有这一切功能却只有区区1M左右的大小.我们平时用的那些linux命令就好比是分立式的电子元件,而busybox就好比是一个集成电路,把常用的工具和命令集成压缩在一个可执行文件里
3:文件系统开始运行busybox
上一章说明从内核如何到busybox, 通过调用linuxrc : 内核文件系统-kernel部分
linuxrc的使用的位置位置
//init.c busybox-1.7.0\init 26088 2007/8/24 665
int init_main(int argc, char **argv)
{
...
if (getpid() != 1
&& (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))
) {
bb_show_usage();
}
其中applet_name是主函数传递进去的参数
const char *applet_name;
#include <stdio.h>
#include <stdlib.h>
#include "usage.h"
int main(int argc, char **argv)
{
applet_name = argv[0];
return APPLET_main(argc,argv);
}
4:文件系统的工作流程
内核启动文件系统中,文件系统的工作流程
1:参数的接收
2:参数的解析
3:参数的应用
Uboot给kernel传递参数是以taglist进行的
kernel给busybox传递参数 parse_inittab
5:解析busybox的主函数init_main
//init.c busybox-1.7.0\init 26088 2007/8/24 665
//运行linuxrc的时候没有额外的参数,所以argc < 1
int init_main(int argc, char **argv)
{
...
/* Set up sig handlers -- be sure to
* clear all of these in run() */
//设置信号的执行函数,信号函数通过run_atcion执行对应cation对的的命令,参考本节5.4
signal(SIGHUP, exec_signal);
signal(SIGQUIT, exec_signal);
signal(SIGUSR1, shutdown_signal);
signal(SIGUSR2, shutdown_signal);
signal(SIGINT, ctrlaltdel_signal);
signal(SIGTERM, shutdown_signal);
signal(SIGCONT, cont_handler);
signal(SIGSTOP, stop_handler);
signal(SIGTSTP, stop_handler);
/* Check if we are supposed to be in single user mode */
if (argc > 1
&& (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))
) {
/* Start a shell on console */
new_init_action(RESPAWN, bb_default_login_shell, "");
} else {
/* Not in single user mode -- see what inittab says */
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
* actions(i.e., runs INIT_SCRIPT and then starts a pair
* of "askfirst" shells */
parse_inittab();
}
5.1:parse_inittab
文件/etc/inittab存在,就是使用文件中的配置信息配置链表结构体
文件不存在就使用代码中默认的信息进行配置链表结构体
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
* actions(i.e., runs INIT_SCRIPT and then starts a pair
* of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
* _is_ defined, but /etc/inittab is missing, this
* results in the same set of default behaviors.
*/
#define INITTAB "/etc/inittab" /* inittab file location */
#ifndef INIT_SCRIPT
#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */
#endif
static void parse_inittab(void)
{
#if ENABLE_FEATURE_USE_INITTAB
FILE *file;
char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
char tmpConsole[CONSOLE_NAME_SIZE];
char *id, *runlev, *action, *command, *eol;
const struct init_action_type *a = actions;
file = fopen(INITTAB, "r");// 打开了INITTAB文件
if (file == NULL) {//文件为空,系统默认设置
/* No inittab file -- set up some default behavior */
#endif
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
/* Swapoff on halt/reboot */
if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
/* Prepare to restart init when a HUP is received */
new_init_action(RESTART, "init", "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
return;
#if ENABLE_FEATURE_USE_INITTAB
}
while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
/* Skip leading spaces */
for (id = buf; *id == ' ' || *id == '\t'; id++);
/* Skip the line if it's a comment */
if (*id == '#' || *id == '\n')
continue;
/* Trim the trailing \n */
//XXX: chomp() ?
eol = strrchr(id, '\n');
if (eol != NULL)
*eol = '\0';
/* Keep a copy around for posterity's sake (and error msgs) */
strcpy(lineAsRead, buf);
/* Separate the ID field from the runlevels */
runlev = strchr(id, ':');
if (runlev == NULL || *(runlev + 1) == '\0') {
message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
continue;
} else {
*runlev = '\0';
++runlev;
}
/* Separate the runlevels from the action */
action = strchr(runlev, ':');
if (action == NULL || *(action + 1) == '\0') {
message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
continue;
} else {
*action = '\0';
++action;
}
/* Separate the action from the command */
command = strchr(action, ':');
if (command == NULL || *(command + 1) == '\0') {
message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
continue;
} else {
*command = '\0';
++command;
}
/* Ok, now process it */
for (a = actions; a->name != 0; a++) { //遍历链表,向id前面添加/dev/
if (strcmp(a->name, action) == 0) {
if (*id != '\0') {
if (strncmp(id, "/dev/", 5) == 0)
id += 5;
strcpy(tmpConsole, "/dev/");
safe_strncpy(tmpConsole + 5, id,
sizeof(tmpConsole) - 5);
id = tmpConsole;
}
new_init_action(a->action, command, id);
break;
}
}
if (a->name == 0) {
/* Choke on an unknown action */
message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
}
}
fclose(file);
#endif /* FEATURE_USE_INITTAB */
}
5.1.1 inittab的格式
下面是关于inittab文件中存放信息的示例的示例
// \busybox-1.7.0\busybox-1.7.0\examples\inittab
# Format for each entry: <id>:<runlevels>:<action>:<process>
action:表示动作,与process向对应
process:要执行的指令
# Note: BusyBox init works just fine without an inittab. If no inittab is
# found, it has the following default behavior:
# ::sysinit:/etc/init.d/rcS //<id> 为空 : <runlevels>为空 : <action> = sysinit : <process> = /etc/init.d/rcS
# ::askfirst:/bin/sh
# ::ctrlaltdel:/sbin/reboot
# ::shutdown:/sbin/swapoff -a
# ::shutdown:/bin/umount -a -r
# ::restart:/sbin/init
5.1.2 new_init_action
将对应的init_action结构体插入到链表init_action_list中
# define bb_dev_null "/dev/null"
static void new_init_action(int action, const char *command, const char *cons)
{
struct init_action *new_action, *a, *last;
if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) //设备为空 && action == ASKFIRST
return;
/* Append to the end of the list */
for (a = last = init_action_list; a; a = a->next) {
/* don't enter action if it's already in the list,
* but do overwrite existing actions */
if ((strcmp(a->command, command) == 0)
&& (strcmp(a->terminal, cons) == 0) //command == 链表的command && cons == a->terminal, 节点在链表中存在
) {
a->action = action; //把action赋值到对应链表a的action上
return;
}
last = a; //没有找到,则last指向链表最后的位置
}
new_action = xzalloc(sizeof(struct init_action)); //分配一个新的action,插入到链表的尾部
if (last) {
last->next = new_action;
} else {
init_action_list = new_action;
}
strcpy(new_action->command, command);
new_action->action = action;
strcpy(new_action->terminal, cons);
messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
new_action->command, new_action->action, new_action->terminal);
}
该链表结构体成员组成和inittab文件对应
struct init_action {
struct init_action *next; //链表指针
int action; //时机 对应<action>
pid_t pid;
char command[INIT_BUFFS_SIZE]; //命令行 对应<process>
char terminal[CONSOLE_NAME_SIZE]; //终端 对应<id>
};
总结参数的传入:
1:用户自定义
/etc/inittab配置文件,在init_main中进行了文件的读取,并且根据文件的每一项参数,创建init_action结构体节点,并且把inittab中所有配置文件解析的init_action节点形成一个相关的结构体链表init_action_list
2:如果用户没有定义 /etc/inittab配置文件,busybox会默认进行多个配置节点的建立,并且形成init_action_list
5.1.3: 默认配置解析
对5.1中没有配置文件的情况下,默认的配置信息进行解析的
1:组成 <id>:<runlevels>:<action>:<process>
static void new_init_action(int action, const char *command, const char *cons)
action对应<action> command对应<command> cons对应<id>
2: 宏的定义
# define CURRENT_VC "/dev/tty0"
# define VC_1 "/dev/tty1"
# define VC_2 "/dev/tty2"
# define VC_3 "/dev/tty3"
# define VC_4 "/dev/tty4"
# define VC_5 "/dev/tty5"
#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */
3: 进行配置
new_init_action(CTRLALTDEL, "reboot", "");
new_init_action(SHUTDOWN, "umount -a -r", "");
new_init_action(RESTART, "init", "");
new_init_action(ASKFIRST, bb_default_login_shell, ""); //bb_default_login_shell = "-/bin/sh"
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
new_init_action(SYSINIT, INIT_SCRIPT, "");
解析为
::CTRLALTDEL:reboot
::SHUTDOWN:umount -a -r
::RESTART:-/bin/sh
/dev/tty2::RESTART:-/bin/sh
/dev/tty3::RESTART:-/bin/sh
/dev/tty4::RESTART:-/bin/sh
::SYSINIT:/etc/init.d/rcS
5.2 参数使用流程 run_action
根据action在链表中找到对定的结构体并执行对应得命令
/* Run all commands of a particular type */
//这个函数就要的作用就是运行和action对应的函数
static void run_actions(int action)
{
struct init_action *a, *tmp;
for (a = init_action_list; a; a = tmp) { //遍历action的链表
tmp = a->next;
if (a->action == action) { //找到action
/* a->terminal of "" means "init's console" */
if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {//terminal不为空,并且读写都OK
delete_init_action(a); //从链表中删除
} else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { //action是SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART
waitfor(a, 0); //等待执行完
delete_init_action(a);//从链表中删除
} else if (a->action & ONCE) {//action是ONCE
run(a); //运行一次
delete_init_action(a);//从链表中删除
} else if (a->action & (RESPAWN | ASKFIRST)) {//action是RESPAWN或者 ASKFIRST
/* Only run stuff with pid==0. If they have
* a pid, that means it is still running */
if (a->pid == 0) { //如果是新进程,action没被运行过
a->pid = run(a); //运行
}
}
}
}
}
5.2.1 waitfor
运行该action对应的命令函数,并且等待其退出
static int waitfor(const struct init_action *a, pid_t pid)
{
int runpid;
int status, wpid;
runpid = (NULL == a)? pid : run(a); //a为空,runpid = pid, a不为空, runpid = run(a)
while (1) {
wpid = waitpid(runpid, &status, 0); //等待runpid退出
if (wpid == runpid)
break;
if (wpid == -1 && errno == ECHILD) {
/* we missed its termination */
break;
}
/* FIXME other errors should maybe trigger an error, but allow
* the program to continue */
}
return wpid;
}
例如
int init_main(int argc, char **argv)
{
...
/* First run the sysinit command */
run_actions(SYSINIT); // 对应的配置(::SYSINIT:/etc/init.d/rcS) 代表运行/etc/init.d/rcS并且等待其退出
/* Next run anything that wants to block */
run_actions(WAIT);
/* Next run anything to be run only once */
run_actions(ONCE);
...
}
5.3 init_main中的while(1)
运行RESPAWN和 ASKFIRST对应的函数命令
int init_main(int argc, char **argv)
{
...
while (1) {
/* run the respawn stuff */
run_actions(RESPAWN); //运行RESPAWN对应的函数命令 "-/bin/sh"
/* run the askfirst stuff */
run_actions(ASKFIRST); //运行ASKFIRST对应的函数命令 -/bin/sh
/* Don't consume all CPU time -- sleep a bit */
sleep(1);
/* Wait for a child process to exit */
wpid = wait(NULL); //等待进程退出。 如果对应函数的进程退出,就再次运行,没有退出就等待(等待输入界面输入命令),当输入命令后再次进入while(1)运行,等待下次输入
while (wpid > 0) {
/* Find out who died and clean up their corpse */
for (a = init_action_list; a; a = a->next) {
if (a->pid == wpid) {
/* Set the pid to 0 so that the process gets
* restarted by run_actions() */
a->pid = 0;
message(L_LOG, "process '%s' (pid %d) exited. "
"Scheduling it for restart.",
a->command, wpid);
}
}
/* see if anyone else is waiting to be reaped */
wpid = waitpid(-1, NULL, WNOHANG);
}
}
}
5.4 再次查看信号对应的函数
signal(SIGINT, ctrlaltdel_signal)
static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
{
run_actions(CTRLALTDEL); //执行CTRLALTDEL(::CTRLALTDEL:reboot)对应的命令reboot 其中CTRLALTDEL:CTRL + ALT + DEL
}
6:通过以上内容,可以得到最小文件系统需要什么:
1:dev/consle (内核初始化调用)
2: init_main函数 (busybox提供)
3: /etc/init.d/rcS (busybox执行的第一个脚本)
4:因为需要运行shell命令,需要shell命令的支持函数 – busybox提供
5:要运行busybox的一些函数(strcmp,strchr,foprn等),需要标准库函数的支持。所以文件系统中必须包含glibc