zoukankan      html  css  js  c++  java
  • 构建最简单的根文件系统

           本文将介绍如何构建一个最简单的根文件系统,并且初步分析内核如何执行第一个内核程序。

        挂接根文件系统

          在挂接根文件系统之前,需要制作根文件系统。根文件系统里面需要一些基本的命令,目录和设备文件,下面来介绍如何使用busybox来制作根文件系统。

          我们都知道,init进程是系统启动后执行的第一个应用程序,根据一般的Linux应用程序配置结构,一个可执行文件通常搭配一个对于的配置文件,例如samba功能对于/etc/samba/smb.conf配置文件,nfs功能对于/dev/exports配置文件,那么init初始化功能肯定也是对于一个配置文件,这个配置文件叫做/etc/inittab.配置文件根据不同的级别,指定在不同的情况下,执行不同的应用程序。

        通过Busybox自带的配置文件格式介绍,我们了解到inittab的文件格式:

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

      id项 : 每一个id会变成 /dev/id ,用作程序的输入输出文件所使用的终端设备,例如id=s3c2410_serial0,则对于终端为s3c2410_serial0

               不需要终端输出的可以设置为空

      runlevels项:用于启动级别的控制,在arm-linux中可省略不写。

      action项: 执行应用程序执行时机,只能设置为几个有效的时机,busybox支持如下几个时间点。

          Valid actions include: sysinit[系统引导时启动], respawn[只要进程一停止,该进程就重新启动], askfirst, wait[进程只阻塞运行一次], once[进程只运行一次], restart, ctrlaltdel, and shutdown.

          askfirst :从名字上可以知道,该触发点是需要先向用户发出提问,看是否需要执行。

          ctraltdel:当用户按下ctrl+alt+del时,内核会向init进程发起SIGINT信号,当init进程接收到此信号,会执行指定的ctrlaltdel类型的应用程序,默认执行reboot命令。

      process:指定应用程序全路径

      如果用户没有在根文件系统中设置/etc/inittab文件,busybox会提供一个默认的配置选项,其等效于如下配置。

      image

    busybox会按照sysinit--->wait---->once---->[respawn--->askfirst----> respawn----> askfirst—>… ]

    除了busybox提供的基本linux文件框架之外,根文件系统还需要程序运行库。

    有了上面的知识铺垫,下面来构建最小根文件系统,那么最小根文件系统里面应该有哪些呢?

    1. /dev/console  . /dev/null

    2. /sbin/init ----> busybox

    3.  /etc/inittab 以及其中定制的应用程序

    4. 运行时的C库

      制作根文件系统的操作步骤:

       1. 配置编译busybox

          我这里选用的是busybox-1.7.0,编译步骤和编译kernel差不多,先make menuconfig,再make ,最后make install。这里面,需要注意如下几点:

           如果在menuconfig中无法配置交叉编译工具,那么需要在 Makefile中修改image,指定特定的工具。

          添加命令行代码补全功能:  Busybox Settings--->Busybox Library Tuning---> Tab completion

          是否选用静态连接: Busybox Settings ----> Build Options ----> Build Busybox as a static binary     一般都设置成动态库

          是否支持热插拔:Linux System Utilities  -----> mdev    

        然后make,执行  make CONFIG_PREFIX=../test_busybox_compile install  ,千万不要直接make install,这样会覆盖PC机上面的原有的代码。安装完成后结果如下:

    image

    busybox编译的结果有bin目录,sbin目录和usr目录,根目录下面还有一个linuxrc链接文件,指向bin/busybox。查看一下所有生成的文件,他们都是链接文件,均指向/bin/busybox。这样的目录构造对于启动一个正常的linux来说还是不够的,下面继续制作。

    2. 添加其他必须的文件

         添加基本的设备节点,仿照当前系统的console设备节点和null设备节点,在busybox的目录下也建立对于的节点。

       image

    3. 构造自己的启动配置文件 /etc/inittab

        为了简单起见,这里面只设置启动时开启shell输入。首先创建etc目录,然后执行如下操作。

    image

      4. 安装C库

        将交叉编译工具链中的所有动态链接库都拷贝到busybox的安装目录中,先创建lib目录,然后执行如下操作:

        本机交叉编译工具安装在/opt目录下,在拷贝C库时要注意,-d选项,这个选项使得拷贝过程中,链接文件保持文件属性不变化,否则,链接文件的内容将是所指向的全部内容,而不在是链接文件本身。

        image

    5.  制作镜像文件

          根文件系统的格式一般有两种,一种是yaffs格式,这种有两类,一类是针对小页512字节的nand flash文件系统,称为yaffs1,另一类是针对大页2K的nand flash文件系统。这里根据硬件的情况,采用的是yaffs2格式的文件系统。

    image

    生成最小根文件系统镜像,然后烧写到板子上去,烧写命令如下:

    tftp 0x30000000 test_busybox_compile_fs.yaffs2

    nand erase root ; nand write.yaffs  0x30000000 0x00260000 $(filesize); reset

    启动后显示如下:

    image ,大功告成。

     

    经过上述几个步骤的操作,一个最简单的根文件系统已经建立好了,此后,可以以此为基础,逐渐添加功能。

      

       附加功能

       上面制作的最小根文件系统有很多不完善的地方,在开机之后,需要自动执行一些挂载命令。

       1。 创建proc目录

            在/etc/inittab里面添加这句话, ::sysinit:/etc/init.d/rcS,使得开机后自动执行rcS脚本文件。

            在rcS脚本文件中,添加  mount –t proc none /proc 就可以开启自动挂载proc虚拟文件系统

       2. 如果需要挂载多个文件系统,推荐使用mount -a选项,这个命令会读取etc/fstab配置文件内的挂载命令。

    image

       此时,修改rcS中的文件,改成mount -a,在etc/fstab目录下创建上述文件内容。

       3. 使能热插拔功能 mdev

         根据Busybox中的mdev.txt中的说明,我们知道,使能mdev功能需要做如下操作:下述操作均在根文件系统跟目录

          mkdir sys & mount –t sysfs sysfs /sys            # mdev通过sysfs文件系统获得设备信息

          mount –t tmpfs mdev /dev                           # 使用内存文件系统,减少对flash的读写

          mkdir /dev/pts                                           # devpts支持外部网络连接(telnet)的虚拟终端

          mount –t devpts devpts /dev/pts                   

          echo /bin/mdev > /proc/sys/kernel/hotplug      # 设置内核热拔插事件回调程序

           mdev –s                                                    #在/dev目录下,生成内核目前支持的所有设备节点

        上述挂载相关的命令可以写在fstab中,其他执行命令可以写在rcS启动脚本中,最后结果如下:

         fstab:

    image

       rcS:

    image

      按照上述操作进行制作烧写后,一个基本架构比较完整的根文件系统就全部建立好了。

     

      知识介绍:mdev工作原理

       执行mdev -s时,mdev扫描/sys/class 和 /sys/block中两个目录下的dev属性文件,从该dev属性文件中,获取设备编号,并以包含该dev属性文件的目录名称作为设备名,device_name。而/sys/class 和 device_name 之间的那部分目录成为subsystem,

               [subsystem] [device_name] dev

    例如:cat  /sys/class/tty/tty0/dev    里面的内容为4:0 【major:minor】,那么subsystem为tty,device_name为tty0

          mdev会根据此信息,在dev目录下创建相应的设备文件

    image

       当mdev因uevent事件(以前叫hotplug事件)被调用时,mdev通过由uevent事件传递给它的环境变量获得到两个变量,

       一个是  引发该uevent事件的action

        另一个是 该设备所在的device path。

        mdev判断action,若是add,则表示有新设备(虚拟设备或者是物理设备)加入系统,mdev会通过设备路径下的dev属性文件获得设备编号,然后根据路径相关信息在/dev目录下建立设备节点。若动作是remove,即表示设备已从系统中移除,则删除/dev/目录下的设备节点。

       由上可知,要想发挥mdev自动创建设备节点的功能,必须有三个条件。

       1. 在/sys/class/的某一个subsystem下   

       2. 在subsystem下创建一个以设备名device_name作为名称的目录

       3. 在此目录里面,包含一个dev属性文件,内容以”major:minor ”的形式输出设备编号。

        一个类class可以看成是一个容器,这个大容器里面包含并管理很多class_device,每一个class_device都对于着一个具体的设备。

       优化操作:

        按照上面制作出来的根文件系统,还有很多可以优化的地方,比如说,可以按照实际需要裁剪文件系统中的C库,可以使用arm-linux-strip优化lib下面的动态链接库和bin/busybox

     

    如何执行第一个程序

       1:  static int noinline init_post(void)
       2:  {
       3:      free_initmem();
       4:      unlock_kernel();
       5:      mark_rodata_ro();
       6:      system_state = SYSTEM_RUNNING;
       7:      numa_default_policy();
       8:   
       9:      /*hao: open console device */
      10:      if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
      11:          printk(KERN_WARNING "Warning: unable to open an initial console.
    ");
      12:   
      13:      (void) sys_dup(0);  /*hao: copy an opened file num,make two file num pointed to the same file.  */
      14:      (void) sys_dup(0);  /*make 1(standard output) and 2(error output) point to 0(standard input)*/
      15:   
      16:      if (ramdisk_execute_command) {
      17:          run_init_process(ramdisk_execute_command);
      18:          printk(KERN_WARNING "Failed to execute %s
    ",
      19:                  ramdisk_execute_command);
      20:      }
      21:   
      22:      /*
      23:       * We try each of these until one succeeds.
      24:       *
      25:       * The Bourne shell can be used instead of init if we are
      26:       * trying to recover a really broken machine.
      27:       */
      28:       /*
      29:       hao: execute_command equal the bootargs named init.  e.g : init=/linuxrc
      30:        then execute_command= = "/linuxrc
      31:        "*/
      32:      if (execute_command) {
      33:          run_init_process(execute_command);
      34:          printk(KERN_WARNING "Failed to execute %s.  Attempting "
      35:                      "defaults...
    ", execute_command);
      36:      }
      37:   
      38:      /*hao: if we don't define init cmd, then we call /sbin/init to start system. */
      39:      run_init_process("/sbin/init");
      40:      run_init_process("/etc/init");
      41:      run_init_process("/bin/init");
      42:      run_init_process("/bin/sh");
      43:   
      44:      panic("No init found.  Try passing init= option to kernel.");
      45:  }

         在内核启动的后面,执行init_post,里面会打开一个终端设备(这里指的是/dev/console),然后将标准输出文件和错误输出文件指向标准输入文件所指向的console设备中去。

         ramdisk_execute_command为uboot传给kernel的rdinit参数,这里为空,所以ramdisk_execute_command也为空。

         execute_command为uboot传给kernel的init参数,这里为/linuxrc,所以execute_command=linuxrc。

         如果uboot没有定义rdinit和init参数,那么kernel会搜索并依此执行/sbin/init,/etc/init…直到某一个程序执行成功,如果都没有找到,则会显示kernel panic,打印对应的提示信息。

     

    参考链接:

    BusyBox简化嵌入式Linux系统

  • 相关阅读:
    查找文件的绝对路径
    购买成都二手商品房交易流程(卖方存在欠银行贷款的情况)
    针对CCTV摄像头的扫描爆破工具 :Cameradar
    交换机的基本配置
    思科交换机和路由器的远程配置
    安装Windows和Ubuntu双系统--Ubuntu安装过程识别不了硬盘
    SpringBoot 配置
    Tomcat9在CentOS7上启动慢解决办法,实测可行
    Linux安装JDK
    电影分享——《小丑》
  • 原文地址:https://www.cnblogs.com/cherishui/p/4237694.html
Copyright © 2011-2022 走看看