当前位置:首页 > 芯闻号 > 充电吧
[导读]整体框架:1)在Linux kernel的源代码中,对如何启动应用程序有着明确的定义。首先我们需要挂载根文件系统,只有正确挂载了根文件系统,才能够从根文件系统中读出应用程序。我们启动的第一个程序就是i

整体框架:


1)在Linux kernel的源代码中,对如何启动应用程序有着明确的定义。首先我们需要挂载根文件系统,只有正确挂载了根文件系统,才能够从根文件系统中读出应用程序。我们启动的第一个程序就是init程序。init进程完成了对应用程序的各项配置(进程ID、执行时机、命令、终端、下一个执行的进程等),并最终依据配置执行了应用程序。 2)要执行应用程序,首先进行配置。配置文件inittab里有着对应用程序的详细配置,这些都是C文件。init进程读出配置、分析配置并配置应用程序、配置C库(用到很多C库里的函数)。最后执行程序。 3)Busybox是一个遵循GPL v2协议的开源项目。Busybox将众多的UNIX命令集合进一个很小的可执行程序中,可以用来替换GNU fileutils、shellutils等工具集。Busybox中各种命令与相应的GNU工具相比,所能提供的选项较少,但是能够满足一般应用。Busybox为各种小型的或者嵌入式系统提供了一个比较完全的工具集。更多详细介绍参考README。

我们执行命令的时候实际是执行busybox 命令

我们查看软连接


内核检测根文件系统并启动init

内核启动的最后一步就是启动init进程,代码在init/main.c/init_post函数

static int noinline init_post(void)
{
    free_initmem();
    unlock_kernel();
    mark_rodata_ro();
    system_state = SYSTEM_RUNNING;
    numa_default_policy();

    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        printk(KERN_WARNING "Warning: unable to open an initial console.n");

    (void) sys_dup(0);
    (void) sys_dup(0);

    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %sn",
                ramdisk_execute_command);
    }

    /*
     * We try each of these until one succeeds.
     *
     * The Bourne shell can be used instead of init if we are
     * trying to recover a really broken machine.
     */
    if (execute_command) {
        run_init_process(execute_command);
        printk(KERN_WARNING "Failed to execute %s.  Attempting "
                    "defaults...n", execute_command);
    }
    run_init_process("/sbin/init");
    run_init_process("/etc/init");
    run_init_process("/bin/init");
    run_init_process("/bin/sh");

    panic("No init found.  Try passing init= option to kernel.");
}

内核启动init进程的过程如下: (1)打开标准输入、标准输出、标准错误设备open("/dev/console") 尝试打开/dev/console设备文件,如果成功即为init进程标准输入设备。(void) sys_dup(0); (void) sys_dup(0);将文件描述符0复制给文件描述符1、2,所以标准输入、输出、错误都对应同一个文件(设备) (2)如果execute_command变量指定了要运行的程序,启动它。

  if (execute_command) {
    run_init_process(execute_command);
  }

其中execute_command为命令行参数,在我们uboot传给内核的参数中,init设置了init=/linuxrc,所以这里的execute_command就等于/linuxrc。
 如果传值成功则执行run_init_process,否则打印printk(KERN_WARNING “Failed to execute %s.  Attempting “”defaults…n”, execute_command);
 并接着往下执行,接着检测其他位置的init进程,若成功则执行,失败则接着往下检测,直到找到合适的init进程或者没找到则打印panic(“No init found.  Try passing init= option to kernel.”);

那么这里我们可以先使用nand erase root擦除root分区,也就是说擦除根文件系统,然后启动只有bootloader和kernel的系统。在结果是否和代码中说明的一致,结果Linux kernel在启动过程中,打印出了如下的信息:

VFS: Mounted root (yaffs filesystem).  
Freeing init memory: 140K
Warning: unable to open an initial console.
Failed to execute /linuxrc.  Attempting defaults...
Kernel panic - not syncing: No init found.  Try passing init= option to kernel.

首先是VFS:挂载了根文件系统,可能大家会问,不是刚刚已经擦除了根文件系统,为什么说这里挂载了?
 这是因为当我们擦除了根文件系统的root分区后,Linux kernel认为它是任意格式的根文件系统(其实分区里面什么都没有),而默认的又是yaffs格式,所以这里说挂载了yaffs格式的根文件系统。
 这里的warning难道不是和我们init_post函数中的printk(KERN_WARNING “Warning: unable to open an initial console.n”);相对应吗?
 同理,Failed to execute /linuxrc.  Attempting defaults…和printk(KERN_WARNING “Failed to execute %s.  Attempting “”defaults…n”, execute_command);相对应。
 Kernel panic - not syncing: No init found.  Try passing init= option to kernel.和panic(“No init found.  Try passing init= option to kernel.”);相对应。
 so=>这证明我们的分析是正确的。

Busybox init进程的启动过程

其中与构建根文件系统关系密切的是控制台的初始化、对inittab文件的解释执行。

内核启动init进程时已经打开“/dev/console”设备作为控制台,一般情况下Busybox init程序就使用/dev/console。
 但是如果内核启动init进程的同时设置了环境变量CONSOLE或console,则使用环境变量所指定的设备。
 在Busybox init程序中,还会检查这个设备是否可以打开,如果不能打开则使用”/dev/null”。

/etc/inittab文件的相关文档和示例代码都在Busybox的examples/inittab文件中,我们来一探究竟

查看inittab文件得知inittab格式:

Format for each entry:
#:::#id:        The id field is used by BusyBox init to specify the controlling tty for the specified process to run on.  
#runlevels:  The runlevels field is completely ignored.
#action:     Valid actions include:   sysinit, respawn, askfirst, wait, once,
#                                            restart, ctrlaltdel, and shutdown.

#process:    Specifies the process to be executed and it's command line.
/*******************************解析************************************/
从默认的new_init_action反推出默认的配置文件:
# inittab格式:
#:::# id => /dev/id,用作终端:stdin,stdout,stderr:printf, scanf, err(即标准输入、输出、错误设
# 备),如果省略,则使用与Init进程一样的控制台。
# runlevels : 忽略 
# action      :执行时机 sysinit, respawn, askfirst, wait, once,
#                        restart, ctrlaltdel, and shutdown.
# process     :应用程序或脚本,如果前有“-”字符,这个程序被称为“交互的”。

在init_main函数中,调用了parse_inittab函数来读取配置文件inittab。如果根文件系统中没有/etc/inittab文件,Busybox init程序将使用默认的inittab条目。这里我们可以通过默认的配置语句,倒推出默认的配置文件内容。

DIR: init.c-parse_inittab函数
     /* 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 HUP 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, "");
/*******************************解析************************************/
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
::restart:/sbin/init
::askfirst:-/bin/sh
tty2::askfirst:-/bin/sh
tty3::askfirst:-/bin/sh
tty4::askfirst:-/bin/sh 
::sysinit:/etc/init.d/rcS

这里涉及到了一个函数 new_init_action 。 它实际上的工作就是把各个程序的执行时机、命令行、控制台参数分别赋值给结构体,并把这些结构体组成一个单链表。这也就是我们所说的配置。 它的声明是:static void new_init_action(int action, const char *command, const char *cons);这三个参数不正是inittab配置文件中的配置命令吗?他们分别对应于来看看new_init_action函数:

DIR:init.c-new_init_action函数

static void new_init_action(int action, const char *command, const char *cons)
{
    struct init_action *new_action, *a, *last;

    if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
        return;

    /* Append to the end of the list */
    for (a = last = init_action_list; a; a = a->next) {
        /* don't enter action if it's already in the list,
         * but do overwrite existing actions */
        if ((strcmp(a->command, command) == 0)
         && (strcmp(a->terminal, cons) == 0)
        ) {
            a->action = action;
            return;
        }
        last = a;
    }

    new_action = xzalloc(sizeof(struct init_action));
    if (last) {
        last->next = new_action;
    } else {
        init_action_list = new_action;
    }
    strcpy(new_action->command, command);
    new_action->action = action;
    strcpy(new_action->terminal, cons);
    messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'n",
        new_action->command, new_action->action, new_action->terminal);
}

/* Set up a linked list of init_actions, to be read from inittab */
struct init_action {
   struct init_action *next;
   int action;
   pid_t pid;
   char command[INIT_BUFFS_SIZE];
   char terminal[CONSOLE_NAME_SIZE];
  };

new_init_action函数用于配置,参数为执行时机、命令行、控制台。
 结构体指针new_action开始指向上一个配置过的程序(其存储在结构体,参数是上一个程序的执行时机、命令行、控制台),这里首先进行了一个If判断,如果控制台等于bb_dev_null(宏定义等于 /dev/null)且action为ASKFIRST那么直接返回,不进行任何配置。
 接着这个for循环算是这个函数的一个重点吧,首先令结构体指针init_action_list赋值给a和last。
 这里的init_action_list(宏定义为NULL)开始为NULL,后来指向第一个配置的程序。
 也就是说,遍历所有配置过的程序,如果这个程序之前被配置过(命令行和控制台同时等于当前遍历的程序),那么执行时机action被重新赋值为当前值。
 通俗的说,这个for为了避免程序重复配置,查找之前配置过的程序有没有当前要配置的程序,如果有,则只改变其执行时机action。命令行和控制台不变。
 如果没有,接下来为new_action重新分配内存,并且给它赋值,令它的各项信息等于当前的程序。在上面的if语句中,last->next=new_action,也就是说,将所有程序的配置结构体连成一个单链表。

new_init_action函数讲解完毕。

经过上面的讲解,我们明白了Linux根文件系统中,对于程序的配置是在parse_inittab函数完成的,它打开配置文件inittab,将程序信息一一填入结构体init_action,并将它们连接成单链表。现在配置已经完成,下一步是执行了。接着看init_main中的代码是怎样执行应用程序的:

busybox-> init_main
            parse_inittab
                file = fopen(INITTAB, "r"); //打开配置文件/etc/inittab

                new_init_action     // ① 创建一个init_action结构,填充
                                    // ② 把这个结构放入init_action_list链表
            run_actions(SYSINIT);
                waitfor(a, 0);      // 执行应用程序,等待它执行完毕
                    run(a)          // 创建process子进程
                    waitpid(runpid, &status, 0); // 等待它结束
                delete_init_action(a);// 在init_action_list链表里删除             
            run_actions(WAIT);
                waitfor(a, 0);      // 执行应用程序,等待它执行完毕
                    run(a)          // 创建process子进程
                    waitpid(runpid, &status, 0); // 等待它结束
                delete_init_action(a);// 在init_action_list链表里删除 
            run_actions(ONCE);
                run(a);
                delete_init_action(a);
            while(1) {
                run_actions(RESPAWN);
                    if (a->pid == 0) {
                        a->pid = run(a);
                    }
                run_actions(ASKFIRST);
                    if (a->pid == 0) {
                        a->pid = run(a);
                                打印:Please press Enter to activate this console.
                                等待回车
                                创建子进程
                    }
                wpid = wait(NULL);  // 等待子进程退出
                while (wpid > 0) {
                    a->pid = 0;     // 退出后,就设置pid=0
                }
            }

在/etc/inittab文件的控制下,init进程的行为总结如下: ① 在系统启动前期,init进程首先启动为sysinit、wait、once的3类子进程。 ② 在系统正常运行期间,init进程首先启动为respawn、askfirst的两类子进程。 ③ 在系统退出时,执行为shutdown、restart、ctrlaltdel的三类子进程(之一或全部) 从而我们可以总结出来最小的根文件系统由5部分组成: 1./dev/console or/dev/null 2.init => busybox 3./etc/inittab 4.配置文件指定的程序 5.C库 busybox的配置、编译和安装 打开busybox自带的INSTALL文件查看我们该怎样配置、编译和安装busybox。

Building:
=========

The BusyBox build process is similar to the Linux kernel build:

  make menuconfig     # This creates a file called ".config"
  make                # This creates the "busybox" executable
  make install        # or make CONFIG_PREFIX=/path/from/root install

The full list of configuration and install options is available by typing:

  make help

1.配置 进入busybox文件夹make menuconfig生成配置文件.config

2.编译

由于我们文件系统是给嵌入式板子用的,先修改Busybox的Makefile,使用交叉编译器。

修改前

ARCH        ?= $(SUBARCH)
CROSS_COMPILE   ?=

修改后

ARCH        ?= $(SUBARCH)
CROSS_COMPILE   ?= arm-linux-

然后make3.安装 注意:如果你是在虚拟机上安装busybox,安装不可直接执行make INSTALL,必须在虚拟机下自己创建一个文件夹,将安装路径指向这个文件夹的路径。再执行make CONFIG_PREFIX=dir_path install否则会破坏系统。 注:除bin/busybox外,其他文件都是到bin/busybox的符号连接。busybox是所有命令的集合体,这些符号连接文件可以直接运行。比如在开发板上,运行“ls”命令和”busybox ls”命令是一样的。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭