嵌入式Linux使用Busybox init进程启动过程分析

2019-07-13 04:29发布

转自:http://blog.csdn.net/shanzhizi/article/details/39082495 一、Busybox     Busybo是一个遵循GPLv2协议的开源项目。Busybox将众多的Linux命令集合进一个很小的可执行程序中,可以用来替换GNU fileutils shellutils等工具集。Busybox中各种命令与相应的GNU工具相比,所能提供的选项较少,但是能够满足一般应用。Busybox为各种小型 的或者嵌入式系统提供了一个比较完完全的工具集。
Busybox在编写过程中对文件大小进行了优化,并考虑了系统资源有限的情况。与一般的GNU 工具集动辄几MB的体积相比,动态连接的Busybox只有几百KB,即使静态连接也只有1MB左右。有人将Busybox比喻成Linux工具中的瑞士 军刀,简单的说就是好像是Linux的一个大的工具集,包括了Linux中的大部分命令和工具。嵌入式根目录下的bin,sbin和usr目录以及 linuxc通常就是Busybox。Busybox会根据配置的不同自动的生成一些文件,但是有些根文件系统下的文件还是需要用户自己来建立。 二、Busybox启动流程分析    init进程是由内核启动的第一个也是惟一的一个用户进程,它根据配置文件决定启动哪些程序,比如执行某些脚本,启动shell,运行用户指定的程序等。 init进程是后续所有进程的发起者,比如init进程启动/bin/sh程序后,才能够在控制台上输入各种命令。
   init进程的执行程序通常是sbin/init,上面讲述的init进程的作用只不过是/sbin/init这个程序的功能。在嵌入式领域,通常使用 Busybox集成的init程序.嵌入式根目录下的bin,sbin和usr目录以及linuxc通常就是Busybox。 1、在kernel/init/main.c的init函数中有如下代码:
 if(execute_command)
 execve(execute_command,argv_init,envp_init);
 execve("/sbin/init",argv_init,envp_init);
bootloader会传给内核的main函数 init=/linuxrc这个参数,于是就会执行下面的这句
execute_command = "linuxrc",busybox中_install目录下的linuxrc是Busybox的一个软链接,指向/bin/busybox,而 /sbin/init也是/bin/busybox的符号链接,因此这个linxrc基本没有实际的意义只是一个连接作用。我们可以重写linuxrc, 添加自己的一些初始化的东西。这样就可以把Linux内核中的init程序和Busybox中的init程序结合起来了。 2、Busybox init进程启动流程    Busybox是目标板系统上执行的第一个应用程序,当调用Busybox它会执行Busybox自身的init进程。
 Busybox initt 程序对应的代码在init/init.c文件中。其对应的流程图如下:     busybox初始化流程图
    其中与构建根文件系统关系密切的是控制台的初始化,对inittab文件的解释及执行。从图中我们可以 看出,Busybox init启动的第一个函数是int init_main(int argc UNUSED_PARAM, char **argv),在这里可以设置信号的处理函数,初始化控制台,最重要的是解析inittab中内容。  在init_main()函数中会调用parse_inittab(void)函数,parse_inittab(void)函数可以使用一些默认的配置,当/etc/inittab没有配置时。 static void parse_inittab(void)
{
#if ENABLE_FEATURE_USE_INITTAB
    char *token[4];
    parser_t *parser = config_open2("/etc/inittab", fopen_for_read);

    if (parser == NULL)
#endif
    {
        /* 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 QUIT is received */
        new_init_action(RESTART, "init", "");
        /* Askfirst shell on tty1-4 */
        new_init_action(ASKFIRST, bb_default_login_shell, "");
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users

        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;
 }
..............   
}
    其中最重要的一个是其中,最重要的一个,就是 new_init_action(SYSINIT, INIT_SCRIPT, ""),也就决定了接下去初始化的脚本是INIT_SCRIPT所定义的值。这个宏的默认值是"/etc/init.d/rcS".
  #define INITTAB "/etc/inittab"    /* inittab file location */
#ifndef INIT_SCRIPT
#define INIT_SCRIPT "/etc/init.d/rcS"    /* Default sysinit script. */
#endif
  1)下面是分析文件系统中/etc/init.d/rcS的内容,这是我的rcS文件中的内容。这个文件会在inittab中使用。在inittab后启动rcS. #!/bin/sh 
PATH=/sbin:/bin:/usr/sbin:/usr/bin 
runlevel=S 
prevlevel=N 
umask 022 
export PATH runlevel prevlevel  //上面几句为启动环境设置必要的环境变量
echo "----------munt all----------------" 
mount -a  //加载文件/etc/fstab文件中的选项,
echo /sbin/mdev>/proc/sys/kernel/hotplug 
mdev -s  //在/dev 目录下建立必要的设备节点;
echo "***********************************************" 
echo "****************Studying ARM Embedded *********************
echo "Kernel version:linux-2.6.32.1" 
echo "Author frank" 
echo "Date:2010.4.19" 
echo "***********************************************
/bin/hostname -F /etc/sysconfig/HOSTNAME  //设置主机的名字   //下面这一句是设置内核的hotplug handler 为 mdev, 即当设备热插拔时,由 mdev 接收来自内核的消息并作出相应的回应, 比如挂载U盘。 echo /sbin/mdev>/proc/sys/kernel/hotplug 2)下面是inittab文件的分析:如果存在/etc/inittab文件,Busybox init程序解析它,然后按照它的指示各种子进程,否则使用默认的配置创建子进程
#etc/inittab
::sysinit:/etc/init.d/rcS    //作为系统初始化文件.
s3c2410_serial0::askfirst:-/bin/sh  在串口启动一个登录会话
::ctrlaltdel:/sbin/reboot  //作为init重启执行程序.
::shutdown:/bin/umount -a –r
   //告诉init在关机时运行umount命令卸载所有的文件系统,如果卸载失败,试图以只读方式重新挂载。 /etc/inittab 文件中每个条目用来定义一个子进程,并确定它的启动方法,格式如下 :
:::
例如:
ttySAC0:askfirst:-/bin/sh
(1):表示这个进程要使用的控制台(即标准输入、标准输出、标准错误设备)。如果省略,则使用与init进程一样的控制台。
(2):对于Busybox init程序,这个字段滑意义,可以省略。
(3):表示init程序如何控制这个子进程,
(4): 要执行的程序,它可以是可执行程序,也可以是脚本
如果:字段有"-"字符,表示这个程序被称为“交互的”。在/etc/inittab/文件的控制下,init进程的行为总结如下:
(1)在系统启动前期,init进程首先启动为sysinit wait once的3类子进程。
(2)在系统正常运行期间,init程序首先启动,为respawn askfirst的两类子进程,并监视它们,发现某个子进程退出时重新启动它。
(3) 在系统退出时,执行 为shutdown restart ctrlaltdel的3类子进程之一或全部。
如果根文件系统中没有/etc/initab文件,Busybox init程序将使用如下默认的inittab条目。              /etc/inittab文件中字段的意义 Action 名称 执行条件 说明 Sysinit 系统启动后最先执行 只执行一次, init 进程等待它结束才继续执行其它动作 Wait 系统执行完 sysinit 进程后 只执行一次, init 进程等待它结束才继续执行其它动作 Once 系统执行完 wait 进程后 只执行一次 ,init 进程不等待它结束 Respawn 启动完 once 进程后 Init 进程监测发现子进程退出时,重新启动它 Askfirst 启动完 respawn 进程后 与 respawn 类似,不过 init 进程先输出“ Please press Enter to actvie this console ”,等用户输入驾车键之后才启动子进程 Shutdown 当系统关机时 即重启关闭系统命令时 Restart Busybox 中配置了CONFIG_FEATURE_USE_INITTAB,并且 init 进程接收到 SIGHUP 信号时 先重新读取,解析 /etc/initab 文件,再执行 restart 程序 Ctrlatldel 按下 Ctr+Alt+del 组合键时   3)下面是etc/fstab文件内容,表示执行完“mount -a”命令后将挂载proc tmpfs等系统   #device mount-point type option dump fsck order
proc /proc proc defaults 0 0
temps /tmp rpoc defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
4)/etc/profile文件 
这个文件是 sh 用的,当用户获得一个 shell 后, sh 就会根据这个文件配置用户的登陆环境,下面是我的profile 文件。 # Ash profile
# vim: syntax= sh
# No core file by defaults
# ulimit - S - c 0> / dev/ null 2> & 1
USER= "id -un" 
LOGNAME= $ USER
PS1= '[/u@/h=W]#' 
PATH= $ PATH
HOSTNAME= '/bin/hostname' 
export USER LOGNAME PS1 PATH
    其中PATH环境变量指定当用户键入一个命令时,sh寻找这个命令的路径。PS1指定sh提示符的格式。其它的 export 命令, alias 命令, busybox 里面的 ash 和 bash 非常相似 
    这样,Busybox所需的基本的配置文件就完成了。这也是Busybox最基本的文件,当然还可以配置更多的文件,增强Busybox的功能。