busybox(一)淺析
阿新 • • 發佈:2018-11-13
title: busybox(一)淺析
tag: arm
date: 2018-11-13 23:02:33
---
busybox淺析
原始碼包在busybox-1.7.0.tar.bz2
,一個命令對應著一個c
檔案,執行init
命令,則是有init.c
,有函式init_main
int init_main(int argc, char **argv);
最終的目的是啟動客戶的應用程式,需要指定具體的環境
1. 配置檔案讀取
2. 解析配置檔案
3. 執行使用者程式
help
相關的幫助可以搜尋下/examples
下的檔案,比如搜尋inittab
,裡面有相關說明
#define SYSINIT 0x001 //執行一次等待結束後從連結串列刪除 #define RESPAWN 0x002 //while迴圈執行 #define ASKFIRST 0x004 //while迴圈執行,會列印資訊,等待回車 #define WAIT 0x008 //執行一次等待結束後從連結串列刪除,在SYSINIT後 #define ONCE 0x010 //與SYSINIT 區別在於不等待其執行結束 #define CTRLALTDEL 0x020 //輸入訊號後執行 #define SHUTDOWN 0x040 //輸入訊號後執行 #define RESTART 0x080 //輸入訊號後執行
流程圖
引入
從init_main
函式入口分析,Linux 是按照run_init_process("/sbin/init");
形式呼叫,沒有傳遞引數,所以執行else
分支,解析配置表
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(); }
讀取inittab
parse_inittab();
讀取inittab
配置表,可以搜尋下example
下檢視例子幫助,查閱如下格式
Format for each entry: <id>:<runlevels>:<action>:<process> <id>: WARNING: This field has a non-traditional meaning for BusyBox init! <runlevels>: The runlevels field is completely ignored. <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown. <process>: Specifies the process to be executed and it's command line.
標識 | 作用 |
---|---|
id | 自動加上/dev/的字首,用作終端,stdin,stdout,stderr:printf,scanf,err 可以省略 |
runlevels | 可以忽略 |
action | 指示何止執行 |
process | 應用程式或者可執行指令碼 |
最終執行new_init_action
執行指令碼程式
建立執行指令碼連結串列
for (a = actions; a->name != 0; a++) {
if (strcmp(a->name, action) == 0) {
if (*id != '\0') {
if (strncmp(id, "/dev/", 5) == 0) //這裡為id加上/dev/的字首
id += 5;
strcpy(tmpConsole, "/dev/");
safe_strncpy(tmpConsole + 5, id,
sizeof(tmpConsole) - 5);
id = tmpConsole;
}
new_init_action(a->action, command, id);
break;
}
}
當不存在這個配置表的時候也會有一個預設配置檔案,這裡以預設的其中一個指令碼解析
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
# define VC_2 "/dev/tty2"
#define ASKFIRST 0x004
const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
#define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh"
也就是最終執行
new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
ASKFIRST 執行的時機
-/bin/sh 指令碼程式
/dev/tty2 id終端,加上了/dev/,符合上述描述
分析下new_init_action
函式內部,
- 建立
init_action
結構,包含inittab
中的資訊 - 加入到
init_action_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)
) {
a->action = action;
return;
}
last = a;
}
struct init_action {
struct init_action *next;
int action;
pid_t pid; //對應程序id, process id
char command[INIT_BUFFS_SIZE]; //對應應用程式
char terminal[CONSOLE_NAME_SIZE]; //對應終端
};
由此,可以理解當配置檔案不存在的時候,會去建立配置表
if (file == NULL) {
/* No inittab file -- set up some default behavior */
/* 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;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>↓
建立類似的inittatb ↓
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>↓
::CTRLALTDEL:reboot
::SHUTDOWN:umount -a -r
::RESTART:init
::ASKFIRST:-/bin/sh:
tty2::ASKFIRST:-/bin/sh
tty3::ASKFIRST:-/bin/sh
tty4::ASKFIRST:-/bin/sh
::SYSINIT:/etc/init.d/rcS
執行指令碼
指令碼有多種型別,不同型別執行方式與時機不同
#define SYSINIT 0x001 //執行一次等待結束後從連結串列刪除
#define RESPAWN 0x002 //while迴圈執行
#define ASKFIRST 0x004 //while迴圈執行,會列印資訊,等待回車
#define WAIT 0x008 //執行一次等待結束後從連結串列刪除,在SYSINIT後
#define ONCE 0x010 //與SYSINIT 區別在於不等待其執行結束
#define CTRLALTDEL 0x020 //輸入訊號後執行
#define SHUTDOWN 0x040 //輸入訊號後執行
#define RESTART 0x080 //輸入訊號後執行
run_actions(SYSINIT);
waitfor(a, 0);//執行a,等待執行結束
run(a);//執行建立process子程序
waitpid(runpid, &status, 0);
delete_init_action(a);//刪除連結串列
/* Next run anything that wants to block */
run_actions(WAIT);
waitfor(a, 0);//執行a,等待執行結束
run(a);//執行建立process子程序
waitpid(runpid, &status, 0);
delete_init_action(a);//刪除連結串列
/* Next run anything to be run only once */
run_actions(ONCE);
run(a);
delete_init_action(a);
/* Now run the looping stuff for the rest of forever */
while (1) {//重新執行pid已經退出的子程序
run_actions(RESPAWN);
if (a->pid == 0) { //預設pid為0
a->pid = run(a);}
run_actions(ASKFIRST);
if (a->pid == 0) {
a->pid = run(a);}
//列印"\nPlease press Enter to activate this console. ";,
//等待輸入回車
//建立子程序
wpid = wait(NULL);//等待子程序退出
while (wpid > 0) {
a->pid--;//推出後pid=0
}
}
}
小結
- 開啟終端
dev/console
- 開啟
dev/null
,用作不設定終端id
的時候的定位 - 讀取配置檔案
etc/inittab
,需要存在配置檔案的可執行程式或者指令碼 - 執行指令碼
所以一個最小的根檔案系統必備的一些資源
dev/console
dev/null
sbin/init-------------busybox提供,至少需要這個應用程式,這是linux啟動的第一個應用程式
etc/inittab-----------配置檔案,定義了一些應用程式
配置檔案制定的應用程式----配置檔案指定的應用程式
C庫--------------------應用程式的C庫