下載下傳busybox 1.00 http://busybox.net/downloads/busybox-1.00.tar.bz2
# tar jxvf busybox-1.00.tar.bz2
# cd busybox-1.00
# make defconfig
# make menuconfig 配置
# make
# make install
需要cp到ramdisk的檔案在_install目錄中
下面主要分析一下核心到busybox的啟動流程
在kernel/init/main.c的init函數中有代碼
if (execute_command)
execve(execute_command,argv_init,envp_init);
execve("/sbin/init",argv_init,envp_init);
一般啟動指令行會給出 init=/linuxrc 這個參數,于是就有了
execute_command = "/linuxrc"
busybox中_install目錄下的 linuxrc 是busybox的一個軟連結
而在我的ramdisk中已經被替換成一個shell腳本,其中的代碼是
#!/bin/sh
exec /sbin/init
是以其實這個linuxrc基本沒什麼用處,我就可以把
if (execute_command) execve(execute_command,argv_init,envp_init);
這句代碼注釋掉,于是核心就直接運作/sbin/init了
/sbin/init也是busybox的軟連結,是以接着下來就要看busybox的源代碼了
入口函數main在busybox-1.00/applets/busybox.c中
int main(int argc, char **argv)
{
const char *s;
bb_applet_name = argv[0];
if (bb_applet_name[0] == '-') bb_applet_name++;
for (s = bb_applet_name; *s != '/0';)
{
if (*s++ == '/') bb_applet_name = s;
}
#ifdef CONFIG_LOCALE_SUPPORT
#ifdef CONFIG_INIT
if(getpid()!=1)
#endif
{ setlocale(LC_ALL, ""); }
#endif
run_applet_by_name(bb_applet_name, argc, argv);
bb_error_msg_and_die("applet not found");
}
bb_applet_name = argv[0]
對于execve("/sbin/init",argv_init,envp_init)
bb_applet_name = argv_init[0] = "init"
在進入shell後,執行指令 ls -l 同樣也會調用main函數
這是 argv[0]="ls" argv[1]="-l"
接着是run_applet_by_name(bb_applet_name, argc, argv)
該函數的作用是找到bb_applet_name對應的主函數并執行,主要的代碼如下
if ((applet_using = find_applet_by_name (name)) != NULL)
{
bb_applet_name = applet_using->name;
exit ((*(applet_using->main)) (argc, argv));
}
struct BB_applet * find_applet_by_name (const char *name)
{
return bsearch (name, applets, NUM_APPLETS,
sizeof (struct BB_applet), applet_name_compare);
}
可以看出find_applet_by_name從applets結構中尋找name對應的項。
const struct BB_applet applets[] = {
#define APPLET(a,b,c,d) {#a,b,c,d},
#define APPLET_NOUSAGE(a,b,c,d) {a,b,c,d},
#define APPLET_ODDNAME(a,b,c,d,e) {a,b,c,d},
#ifdef CONFIG_TEST
APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
#endif
#ifdef CONFIG_ADDGROUP
APPLET(addgroup, addgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)
#endif
#ifdef CONFIG_ADDUSER
APPLET(adduser, adduser_main, _BB_DIR_BIN, _BB_SUID_NEVER)
#endif
。。。。}
這樣看來如果想要在busybox中添加相應的指令,就隻需在這裡添加一項并提供
相應的主函數即可。找到我關注的是init項
#ifdef CONFIG_INIT
APPLET(init, init_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
#endif
于是init_main函數被執行。
在busybox-1.00/init/init.c中找到init_main函數,分析一下其中的關鍵代碼
parse_inittab();
run_actions(SYSINIT);
run_actions(ASKFIRST);
主要是這三個調用
parse_inittab函數分析inittab檔案并執行其中的指令
友善一點可以把删除inittab檔案,而我這裡也是沒有inittab檔案的。
以下是沒有inittab檔案執行的代碼
file = fopen(INITTAB, "r");
if (file == NULL) {
new_init_action(CTRLALTDEL, "/sbin/reboot", "");
new_init_action(SHUTDOWN, "/bin/umount -a -r", "");
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
new_init_action(SHUTDOWN, "/sbin/swapoff -a", "");
#endif
new_init_action(RESTART, "/sbin/init", "");
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);
new_init_action(SYSINIT, INIT_SCRIPT, "");
return;
#ifdef CONFIG_FEATURE_USE_INITTAB
}
隻是調用了很多的new_init_action函數,這個函數其實是把這些init_action添加到
以init_action_list為頭的連結清單裡,這樣便可以通過run_actions函數來調用。
run_actions(SYSINIT) 就會執行 INIT_SCRIPT 指令
#define INIT_SCRIPT "/etc/init.d/rcS"
于是就執行了/etc/init.d/rcS這個shell腳本,需要看一下run_actions這個函數
static void run_actions(int action)
{
struct init_action *a, *tmp;
for (a = init_action_list; a; a = tmp)
{
tmp = a->next;
if (a->action == action)
{
if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART))
{
waitfor(a);
delete_init_action(a);
}
else if (a->action & ONCE)
{
run(a);
delete_init_action(a);
}
else if (a->action & (RESPAWN | ASKFIRST))
{
if (a->pid == 0) a->pid = run(a);
}
}
}
}
對于SYSINIT運作的是waitfor(a),而對于ASKFIRST運作的是run(a)
看waitfor函數的代碼知道其實它也調用了run(a)建了一個子程序,隻是
父程序會等待子程序運作結束。run函數是比較長的,隻取其中的關鍵代碼看看
strcpy(buf, a->command);
s = buf;
for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " /t")) != NULL;)
{
if (*tmpCmd != '/0')
{
cmd[i] = tmpCmd;
i++;
}
}
cmd[i] = NULL;
cmdpath = cmd[0];
if (*cmdpath == '-') {
++cmdpath;
s = bb_get_last_path_component(cmdpath);
if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) {
message(LOG | CONSOLE, bb_msg_memory_exhausted);
cmd[0] = cmdpath;
} else {
cmd[0][0] = '-';
strcpy(cmd[0] + 1, s);
}
}
execv(cmdpath, cmd);
其實就是處理a->command來獲的要執行的檔案路徑和argv參數,調用execv函數執行它
對于run_actions(SYSINIT) 其a->command = "/etc/init.d/rcS"
處理後cmdpath = "/etc/init.d/rcS" ,cmd[0] = "/etc/init.d/rcS"
run_actions(ASKFIRST) 其a->command = bb_default_login_shell
const char * const bb_default_login_shell = LIBBB_DEFAULT_LOGIN_SHELL;
#define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh"
a->command = "-/bin/sh"
處理後cmdpath = "/bin/sh" ,cmd[0] = "-sh"
由于run_actions(SYSINIT)會一直等待子程序執行完,
/etc/init.d/rcS就執行完了,基本的初始化也完成了,那麼就可以進入shell了
這裡就是execv(cmdpath, cmd)去執行shell的
一般會要求按回車才進shell,可以把run函數中以下注釋掉來取消回車直接進入
if (a->action & ASKFIRST)
{
char c;
messageD(LOG, "Waiting for enter to start '%s'(pid %d, terminal %s)/n",
cmdpath, getpid(), a->terminal);
bb_full_write(1, press_enter, sizeof(press_enter) - 1);
while(read(0, &c, 1) == 1 && c != '/n')
;
}
/bin/sh同樣是busybox的軟連結,同樣去執行main函數
這是的argv[0] = "-sh" 去掉-後,用"sh"去查找函數
#if defined(CONFIG_FEATURE_SH_IS_ASH) && defined(CONFIG_ASH)
APPLET_NOUSAGE("sh", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
#elif defined(CONFIG_FEATURE_SH_IS_HUSH) && defined(CONFIG_HUSH)
APPLET_NOUSAGE("sh", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
#elif defined(CONFIG_FEATURE_SH_IS_LASH) && defined(CONFIG_LASH)
APPLET_NOUSAGE("sh", lash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
#elif defined(CONFIG_FEATURE_SH_IS_MSH) && defined(CONFIG_MSH)
APPLET_NOUSAGE("sh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER)
#endif
busybox中有四種shell,在配置的時候注意下,一般選擇預設的為ash
這樣執行的函數就是ash_main,ash_main怎樣進入指令行,怎樣接收指令,下次再分析了