zoukankan      html  css  js  c++  java
  • 《构建根文件系统(二)分析busybox源码》

    1.busybox

      平时我们在开发板中输入ls、cp、mv等命令,都是在/bin文件中。而通过ls -l就可以发现

      

      这些命令都是放在busybox中的。并且在内核启动后,通过ps命令,可以看到有一个init进程正在运行。

      

       因此就先来分析一下,这个第一个运行的进程init。

    2.init进程分析

    init_main函数:

    int
    init_main(int argc, char **argv) { ... ... console_init(); //初始化控制台,在init_post()中只是打开了控制台设备 ... ... if (argc > 1 //在init_post()中执行的”_init_process("/sbin/init");”,所以argc=1, argv=/sbin/init && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))) {... ...} //此处省略,因为不执行 else { parse_inittab(); //argc==1,执行else,读取解析init 表(解析配置文件) } .... ... //运行应用程序 }

      因为argc=1,所以直接跳转到parse_inittab。

    parse_inittab函数:
    #if ENABLE_FEATURE_USE_INITTAB
     char *token[4];
     parser_t *parser = config_open2("/etc/inittab", fopen_for_read);
     if (parser == NULL)
    #endif

      首先如果定义了ENABLE_FEATURE_USE_INITTAB的话,就会去读取/etc/inittab的内容。

      那么怎么知道ENABLE_FEATURE_USE_INITTAB有没有被定义?

      可以make menuconfig

      然后查找inittab

      

      进入init utilitire的init

      

       这个就可以了。不过一般默认就是选中的。

      通过在源码中搜索inittab可以找到两个文件,一个是inittab的相关文档说明,一个是inittab的配置文档。

    inittab说明文档:

    # Format for each entry: <id>:<runlevels>:<action>:<process>

      以上说明了inittab的格式。

      id:/dev/id,用作终端,就是我们的标准输入、标准输出、标准错误。

      runlevels:可以忽略。

      action:执行时间。

      process:应用程序或脚本。

    parse_inittab函数:    

    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; }

      如果parser为NULL,那么将会执行默认的操作。也就是如果没有inittab这个配置文件的话,系统也会默认执行一些操作。

       new_init_action(ASKFIRST, bb_default_login_shell, VC_2)以这个为例解析一下:

      #define ASKFIRST    0x10

      bb_default_login_shell是一个被声明的char的变量,查看一下可以发现#define LIBBB_DEFAULT_LOGIN_SHELL     "-/bin/sh"

      # define VC_2 "/dev/tty2"

      所以等价为new_init_action(0x10,   "-/bin/sh"l,"/dev/tty2")

      

      接下来分析一下new_init_action函数:

    new_init_action函数:
    
        struct init_action *a, **nextp;
    
        nextp = &init_action_list;
        while ((a = *nextp) != NULL) {
            /* Don't enter action if it's already in the list,
             * This prevents losing running RESPAWNs.
             */
            if (strcmp(a->command, command) == 0
             && strcmp(a->terminal, cons) == 0
            ) {
                /* Remove from list */
                *nextp = a->next;
                /* Find the end of the list */
                while (*nextp != NULL)
                    nextp = &(*nextp)->next;
                a->next = NULL;
                break;
            }
            nextp = &a->next;
        }    

    /* A linked list of init_actions, to be read from inittab */
    struct init_action {
     struct init_action *next;
     pid_t pid;
     uint8_t action_type;
     char terminal[CONSOLE_NAME_SIZE];
     char command[COMMAND_SIZE];
    };

      定义了一个结构体变量a和nextp,这个结构体的内容其实就是传递进来的参数。然后利用一个链表,进行查询。

      如果init_action_list中已经有了,则跳过。直到查询到最后一个链表。

    new_init_action函数
    
        if (!a)
            a = xzalloc(sizeof(*a));
        /* Append to the end of the list */
        *nextp = a;
        a->action_type = action_type;
        safe_strncpy(a->command, command, sizeof(a->command));
        safe_strncpy(a->terminal, cons, sizeof(a->terminal));    

      如果在init_action_list没有找到,则将传递进来的参数进行填充。

      所以从几个默认的new_init_action可以知道配置文件:

      首先从inittab文档中看到inittab的格式是:

      ::sysinit:/etc/init.d/rcS

      Format for each entry: <id>:<runlevels>:<action>:<process>

       <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
                                  restart, ctrlaltdel, and shutdown.

      所以action是sysinit:   process是/etc/init.d/rcS

      new_init_action(CTRLALTDEL, "reboot", "") ---> ::ctrlaltdel:reboot

      new_init_action(SHUTDOWN, "umount -a -r", "") ---> ::shutdown:umount -a -r    

      new_init_action(RESTART, "init", "") ---> ::restart:init

      new_init_action(ASKFIRST, bb_default_login_shell, "") ---> ::askfirst:-/bin/sh

      new_init_action(ASKFIRST, bb_default_login_shell, VC_2) ---> tty2::askfirst:-/bin/sh

      new_init_action(ASKFIRST, bb_default_login_shell, VC_3) ---> tty3::askfirst:-/bin/sh

      new_init_action(ASKFIRST, bb_default_login_shell, VC_4) ---> tty4::askfirst:-/bin/sh

      new_init_action(SYSINIT, INIT_SCRIPT, "") ---> ::sysinit:/etc/init.d/rcS

      接下来就是对new_init_action完后,然后执行这些操作

      

    init_main函数
    
        /* Now run everything that needs to be run */
        /* First run the sysinit command */
        run_actions(SYSINIT);
        check_delayed_sigs();
        /* Next run anything that wants to block */
        run_actions(WAIT);
        check_delayed_sigs();
        /* Next run anything to be run only once */
        run_actions(ONCE);

      很明显就是运行一些需要开机运行的程序。执行顺序:SYSINIT类、WAIT类、ONCE类。

      run_actions里面的源码就不进去分析了,和new_init_action是一样的。

       接下来就会进入一个while(1)的循环,其中run_actions(RESPAWN | ASKFIRST),还会检测一些信号,然后执行相对应的动作。这个while部分后面再详细分析

    init_main函数
    
    while(1){
            int maybe_WNOHANG;
    
            maybe_WNOHANG = check_delayed_sigs();
    
            /* (Re)run the respawn/askfirst stuff */
            run_actions(RESPAWN | ASKFIRST);
            maybe_WNOHANG |= check_delayed_sigs();
    
            /* Don't consume all CPU time - sleep a bit */
            sleep(1);
            maybe_WNOHANG |= check_delayed_sigs();
    
            /* Wait for any child process(es) to exit.
             *
             * If check_delayed_sigs above reported that a signal
             * was caught, wait will be nonblocking. This ensures
             * that if SIGHUP has reloaded inittab, respawn and askfirst
             * actions will not be delayed until next child death.
             */
            if (maybe_WNOHANG)
                maybe_WNOHANG = WNOHANG;
            while (1) {
                pid_t wpid;
                struct init_action *a;
    
                /* If signals happen _in_ the wait, they interrupt it,
                 * bb_signals_recursive_norestart set them up that way
                 */
                wpid = waitpid(-1, NULL, maybe_WNOHANG);
                if (wpid <= 0)
                    break;
    
                a = mark_terminated(wpid);
                if (a) {
                    message(L_LOG, "process '%s' (pid %d) exited. "
                            "Scheduling for restart.",
                            a->command, wpid);
                }
                /* See if anyone else is waiting to be reaped */
                maybe_WNOHANG = WNOHANG;
            }
        }

    总结:

      我们使用的文件系统的命令都是链接到busybox,而busybox开始都会运行init进程。

      init进程都做了什么事情:

      1.打开/etc/inittab

      2.根据配置文件里面指定的应用程序,去运行。如果没有读取到,init会有默认的配置项

      

      

  • 相关阅读:
    『CEO日报』-商业版的今日头条,《财富》(中文版)出品 on the App Store
    Hosted Web Scraper Online
    名巢靓家_百度百科
    服装消费3.0时代的试验者: Pretty Yes 通过穿搭问答解决中产女性的时尚衣着问题
    好市多_百度百科
    新闻:融资600万 他用一套系统优化15大HR工作场景 精简入转调离 月开通214家 | IT桔子
    眨眼网杨莹,能写代码能玩时尚的美女CEO-搜狐
    新闻:全球独立设计师平台眨眼网推出男装系列 | IT桔子
    漏洞盒子 | 互联网安全测试平台
    浙江设立200亿元省产业基金·杭州日报
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11557781.html
Copyright © 2011-2022 走看看