• busybox里的init


    busybox被大家比作瑞士军刀,主要是它以很小的体积提供给我们很多很有用的shell指令。但是这里我们要关注的是busybox的init。

    内容有些多,我们结合它的init.c的代码做简单介绍(busybox-1.11.2/init/init.c)。

    1、首先busybox的init会尝试只读方式打开inittab,并读取里面的配置:

    static void parse_inittab(void)
    {
        FILE *file;
        char buf[COMMAND_SIZE];
    
        if (ENABLE_FEATURE_USE_INITTAB)
            file = fopen(INITTAB, "r");
        else
            file = NULL;
        ...
        while (fgets(buf, COMMAND_SIZE, file) != NULL) {
            static const char actions[] =
                STR_SYSINIT    "sysinit\0"
                STR_RESPAWN    "respawn\0"
                STR_ASKFIRST   "askfirst\0"
                STR_WAIT       "wait\0"
                STR_ONCE       "once\0"
                STR_CTRLALTDEL "ctrlaltdel\0"
                STR_SHUTDOWN   "shutdown\0"
                STR_RESTART    "restart\0"
            ;
            char tmpConsole[CONSOLE_NAME_SIZE];
            char *id, *runlev, *action, *command;
            const char *a;
    
            /* Skip leading spaces */
            id = skip_whitespace(buf);
            /* Trim the trailing '\n' */
            *strchrnul(id, '\n') = '\0';
            /* Skip the line if it is a comment */
            if (*id == '#' || *id == '\0')
                continue;
    
            /* Line is: "id:runlevel_ignored:action:command" */
            runlev = strchr(id, ':');
            if (runlev == NULL /*|| runlev[1] == '\0' - not needed */)
                goto bad_entry;
            action = strchr(runlev + 1, ':');
            if (action == NULL /*|| action[1] == '\0' - not needed */)
                goto bad_entry;
            command = strchr(action + 1, ':');
            if (command == NULL || command[1] == '\0')
                goto bad_entry;
    
            *command = '\0'; /* action => ":action\0" now */
            for (a = actions; a[0]; a += strlen(a) + 1) {
                if (strcmp(a + 1, action + 1) == 0) {
                    *runlev = '\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((uint8_t)a[0], command + 1, id);
                    goto next_line;
                }
    ...
    }

    从上面,我们可以看到inittab是以#号作为注释,然后其余部分的格式是:id:runlevel_ignored:action:command(留意这里的runlevel_ignored,busybox里面的runlevel_ignored没有意义;也可以说busybox里面只有一个runlevel。)

    而这里一共定义了STR_SYSINIT、STR_RESPAWN、STR_ASKFIRST、STR_WAIT、STR_ONCE、STR_CTRLALTDEL、STR_SHUTDOWN、STR_RESTART等一共8种action。

    分析完inittab文件后,init把他们都放到了init_action_list里面,供后面使用。

    2、执行具体的action是在run_actions()里面。比如,我们要执行SYSINIT,那么,我们就调用run_action(SYSINIT),如此,会从init_action_list里面找到所有标记为SYSINIT的条目,然后调用run来执行它。

    static void run_actions(int action_type)
    {
        struct init_action *a, *tmp;
    
        for (a = init_action_list; a; a = tmp) {
            tmp = a->next;
            if (a->action_type & action_type) {
                /* a->terminal of "" means "init's console" */
                if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
                    //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/);
                    delete_init_action(a);
                } else
                if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
                    waitfor(run(a));
                    delete_init_action(a);
                } else if (a->action_type & ONCE) {
                    run(a);
                    delete_init_action(a);
                } else if (a->action_type & (RESPAWN | ASKFIRST)) {
                    /* Only run stuff with pid==0.  If they have
                     * a pid, that means it is still running */
                    if (a->pid == 0) {
                        a->pid = run(a);
                    }
                }
            }
        }
    }

    这里有两个地方需要留意:

    1)在run()函数里面有调用fork,也就是,run_actions会把每个command都放到一个新的progress里面去执行。

    2)RESPAWN和ASKFIRST是不会从action_list里面删除掉的!!!所以,标记为这两个ACTION的操作,在run之前会检查进程在不在,不在的话就重启,在的话什么都不做,继续执行下一跳。

    3、init_main

    init_main()里面描述了init的动作过程。在parse_inittab之后,动作过程基本如下:

    1)按SYSINIT、WAIT、ONCE,做好init的准备工作。具体比如,run_actions(SYSINIT)。

    2)开始一个死循环。执行RESPAWN和ASKFIRST,如果这些process不存在,就重启他们;监视子进程是否有挂掉的,如果有,就把他们的尸体拾掇拾掇,然后登记在册。

    4.关于/etc/init.d/rcS。在parse_inittab() 里面找到了它的踪迹,它是在找不到inittab的情况下默认执行的动作之一;同时,其它一些实现中,rcS 会在 inittab 中被指定为 sysinit 执行脚本,与其他 inittab 任务并列存在。

    if (ENABLE_FEATURE_USE_INITTAB)
            file = fopen(INITTAB, "r");
        else
            file = NULL;
    
        /* No inittab file -- set up some default behavior */
        if (file == NULL) {
            /* 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, "");
            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;
        }

    这里是讲,如果我们没有定义inittab,我们就在init_action_list里面添加上面罗列的这些action。

    最后一个,INIT_SCRIP 指向的就是/etc/init.d/rcS ,唯一需要修改的 SYSINIT 动作,也就是由 /etc/init.d/rcS 来剩余的系统初始化动作。

    5、inittab编程

    busybox的inittab文件与通常的inittab不同,它没有runlevel的概念,语句功能上也有限制。

    inittab语句的标准格式是<id>:<runlevels>:<action>:<command>

    各字段的含义如下:
    <id>: id字段与通常的inittab中的含义不同,它代表的是这个语句中process执行所在的tty设备,内容就是/dev目录中tty设备的文件名。由于是运行process的tty设备的文件名,所以也不能象通常的inittab那样要求每条语句id的值唯一。
    <runlevels>: 此字段完全被忽略。
    <action>: 为下列这些值之一:
    sysinit, respawn, askfirst, wait,once, restart, ctrlaltdel, shutdown
    其含义与通常的inittab的定义相同。特别提一下askfirst,它的含义与respawn相同,只是在运行process前,会打出一句话 “please press Enter to active this console”,然后等用户在终端上敲入回车键后才运行process。
    <command>: 指定要运行process的命令行,比如下面,我们要在init完成后启动dropbear,那么它的命令行参数是dropbear -F -d /etc/persistent/dropbear_dss_host_key -r /etc/。

    下面是该系统使用的inittab:

    # Executed on startup
    ::sysinit:/etc/rc.d/rc.sysinit
    # Start an "askfirst" shell on the console (whatever that may be)
    ttyS0::askfirst:/sbin/getty -L ttyS0 115200 vt100
    # Stuff to do when restarting the init process
    ::restart:/sbin/init
    # Run daemons
    ::wait:/usr/etc/rc.d/rc start
    # Stuff to do before rebooting
    ::shutdown:/etc/rc.d/rc stop
    ::shutdown:/etc/rc.d/rc.stop
    ::shutdown:/bin/umount -a -r
    null::respawn:/bin/infctld -m -c
    null::respawn:/bin/lighttpd -D -f /etc/lighttpd.conf
    null::respawn:/bin/dropbear -F -d /etc/persistent/dropbear_dss_host_key -r /etc/
    ——————
    无论在哪里做什么,只要坚持服务、创新、创造价值,其他的东西自然都会来的。
  • 相关阅读:
    代码校验工具 SublimeLinter 的安装与使用
    java中写sql语句的小小细节
    搭建Hexo博客并部署到Github
    更改npm全局模块和cache默认安装位置
    笔记本连接老式显示器(VGA线+HDMI接口)
    用JSON-server模拟REST API
    使用 Feed43
    Coding.net+Myeclipse 2014 Git配置
    line-height 属性
    border-style 属性
  • 原文地址:https://www.cnblogs.com/pied/p/2882201.html
Copyright © 2020-2023  润新知