Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1057575
  • 博文数量: 166
  • 博客积分: 10217
  • 博客等级: 上将
  • 技术积分: 2133
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-09 19:45
文章分类

全部博文(166)

文章存档

2012年(3)

2011年(7)

2010年(18)

2009年(59)

2008年(79)

我的朋友

分类: LINUX

2008-11-17 17:15:12

转自:http://blog.csdn.net/dansen_xu/archive/2007/12/03/1914411.aspx

下载busybox 1.00
# 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) /* Do not set locale for `init' */
#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) {
  /* No inittab file -- set up some default behavior */
  /* Reboot on Ctrl-Alt-Del */
  new_init_action(CTRLALTDEL, "/sbin/reboot", "");
  /* Umount all filesystems on halt/reboot */
  new_init_action(SHUTDOWN, "/bin/umount -a -r", "");
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
  /* Swapoff on halt/reboot */
  new_init_action(SHUTDOWN, "/sbin/swapoff -a", "");
#endif
  /* Prepare to restart init when a HUP is received */
  new_init_action(RESTART, "/sbin/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;
#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" /* Default sysinit script. */
于是就执行了/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);
 /* make a new argv[0] */
 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怎样进入命令行,怎样接收命令,下次再分析了
阅读(2777) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~