嵌入式根文件系统分析

1.
      内核相关代码分析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 %s\n",
       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.");
   }

   /dev/console   这里的console为在 uboot传进来的参数bootargs中console=/dev/ttySAC0

   也是 内核打开的 第一个设备

    sys_open((const char __user *) "/dev/console", O_RDWR, 0)

   (void) sys_dup(0);  dup表示复制的意思   打开设备

  (void) sys_dup(0);  dup表示复制的意思   打开设备

  上边三行用于分别定义标准输入.标准输出.标准错误所指向的设备

  execute_command 这个表示从命令行获得的参数代表内核将要执行的第一个程序

   即 :run_init_process(execute_command);

  如果bootargs里没有指定第一个执行程序的话就执行默认的程序中的一个  
   即 :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.");

   该句通过上边的console所定义的设备输出

2. 关于uboot传递的 ”init= str“ 在内核里处理的相关代码如下

     static int __init init_setup(char *str)
  {
   unsigned int i;

   execute_command = str;
   /*
    * In case LILO is going to boot us with default command line,
    * it prepends "auto" before the whole cmdline which makes
    * the shell think it should execute a script with such name.
    * So we ignore all arguments entered _before_ init=... [MJ]
    */
   for (i = 1; i < MAX_INIT_ARGS; i++)
    argv_init[i] = NULL;
   return 1;
  }
  __setup("init=", init_setup);

 其中

    __setup("init=", init_setup);

    就是在 检查bootargs环境变量里边是否有init= str时  
    如果有则 将str赋值给execute_command

3.busybox构建根文件系统 

  当我们编译 busybox时会在 bin目录生成一个应用程序 busybos

  这个busybos是 ls cp。。等命令的组合 而bin目录下的每一个命令均是到 busybox的链接
  即:
      # ls -al /bin/cp /bin/ls 
   lrwxrwxrwx    1 1000     1000            7 Jan  6  2010 /bin/cp -> busybox
   lrwxrwxrwx    1 1000     1000            7 Jan  6  2010 /bin/ls -> busybox


  bin目录下的ls cp 等命令执行时都相当于执行 busybox程序

  而 busybos 会根据命令的不同进行不同的操作

  例如执行ls  相当于执行busybox ls

      执行cp  相当于执行busybox cp

 对于内核执行的默认的第一个程序 /sbin/init  它也是都busybox的一个连接

4.分析busybox源码  分析/sbin/init

     busybox源码里 每一个命令均对应一个 .C 文件  如  ls.c cp.c init.c

     而每个.C文件里都有一个main函数   当我们在终端执行不同的命令时都会调用不同的main函数 

     这些函数都被编译生成busybos程序  根文件系统/bin /sbin里的命令均是执行busybox程序 

    详细分析/sbin/init  

         内核启动的第一个程序   而我们的最终目的是启动我们自己写的用户程序  

         所以有一个配置文件(/etc/inittab)  在这个配置文件里指定了 后续执行哪个应用程序

         init程序的工作

                  :读取配置文件  解析配置文件   (根据配置文件)执行用户程序

                    对应的函数 parse_inittab(); (位于main函数里)

                    内容如下
                       :
                         。。。 

                         file = fopen(INITTAB, "r");打开一个文件 INITTAB  



                                INITTAB 宏定义为 "/etc/inittab"  所以可得该配置文件为 /etc/inittab  

                             inittab的格式   Format for each entry: <id>:<runlevels>:<action>:<process>

                                              例
                                                  ::sysinit:/etc/init.d/rcS
                           ::respawn:-/bin/sh
                           tty2::askfirst:-/bin/sh
                           ::ctrlaltdel:/bin/umount -a -r

                                                分析  id项 用作终端 printf scanf stdio err..

                                                      runlevel 省略 

                                                      action  何时执行  可取为 sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.

                                                      process 对应的想要执行的程序 

                                  后边的 if 语句用于说明 没有/etc/inittable时怎么执行

                                         当没有/etc/table时执行下边代码
                                                           if (file == NULL) {
                                       /* No inittab file -- set up some default behavior */
                                     #endif
                                       /* 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, "");

                                       return;
                                     #if ENABLE_FEATURE_USE_INITTAB
                                      }
                                上边代码就是将生成默认的init_action结构体 并将其加入init_action_list链表       




                         new_init_action(a->action, command, id);  函数分析    

                                   分析该函数  代码如下 

                                   创建init_action 结构

                                           struct init_action *new_action, *a, *last;

                                   将init_action 结构放入 init_action_list 链表   


                                           如果该链表里有一个init_action 结构体 且 command id均与传入的command与id相同

                                           则 覆盖  否则 将该结构体加入 init_action_list链表



                接着这main函数中执行

                             run_actions(SYSINIT);   执行  init_action_list 链表里 action为SYSINIT的proces

                                       waitfor(a, 0);  等待执行完毕

                             run(a);创建 process 子进程

                             wpid = waitpid(runpid, &status, 0); 等待子进程执行结束


                     delete_init_action(a); 从 init_action_list 链表里删除该 action 的结构体

                             run_actions(WAIT);       执行  init_action_list 链表里 action为WAIT的proces


                                            同 SYSINIT

                             run_actions(ONCE);      执行  init_action_list 链表里 action为ONCE的proces

                                            run(a);

                                            delete_init_action(a);

                            while(1)
                            {

                                /* run the respawn stuff */
                  run_actions(RESPAWN);  执行  init_action_list 链表里 action为 RESPAWN 的proces

                 /* run the askfirst stuff */
                 run_actions(ASKFIRST); 执行  init_action_list 链表里 action为ASKFIRST 的proces

                 wpid = wait(NULL);

                   while (wpid > 0) {
                      a->pid = 0;

                        }

                             }  


                run_actions(action);函数分析             


总结  

   最小根文件系统需要的内容

  /dev/console  /dev/null(当不设置程序的id时 程序的标准输入,标准输出,标准错误,将会定位到/dev/null里)

  /etc/inittab(配置文件里指定了 process 我们要执行的用户process就在这里) 

  库   (init的很多函数都依靠 c库)  

  /sbin/init程序(指向busybox命令)
  


5.构建根文件系统


   编译busybox  

             配置编译工具  在顶层的makefile里配置

        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 CONFIG_PREFIX= xxx  install        # or make CONFIG_PREFIX=/path/from/root install    

                安装到指定的目录下
          得到如下结果    
book@book-desktop:/work/nfs_root/my_fs$ ls 
                  bin  linuxrc  sbin  usr

                 得到/sbin/init程序
book@book-desktop:/work/nfs_root/my_fs/sbin$ ls init -l
                  lrwxrwxrwx 1 book book 14 2013-08-02 23:28 init -> ../bin/busybox

   创建设备  console  null

                mknod console c 5 1   创建字符设备 console 主设备号5 次设备号1  c表示字符设备

               mknod null c 1 3

  创建 /etc/inittab        

       mkdir etc 
       cd etc/
       vi inittab 
       添加console::askfirst:-/bin/sh 

   安装c库 
           mkdir lib

           cd /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib

           cp *.so* /work/nfs_root/my_fs/lib -d  
                 *.so*为所以的动态库
                 .a  为静态库  这里只拷贝静态库  因为在 make menuconfig 配置时配置的动态库

             -d 表示连接文件仍拷贝成连接文件 不加-d 时 连接文件将会变成真实内容 照成根文件系统过大     
至此最小根文件系统作成

完善最小根文件系统

         1.  proc  将内核提供的虚拟文件系统挂接在这里  执行ps命令时会到这里提取信息 

           # mkdir  proc 

           可以手动挂载 也可以 在写入inittab 启动自动挂载

           方法 1   新建一个脚本 将其加入inittab文件   如下 

           vi /etc/init.d/rcS

           在rcS脚本里写入 mount -t proc none /proc

           chmod +x  /etc/init.d/rcS

           然后在inittab 添加 ::sysinit:/etc/init.d/rcS

           类同可以添加其它命令 

           方法 2 使用 mount -a  同样在 /etc/init.d/rcS 里添加 mount -a 

                 改命令依赖于/etc/fstab

                 fstab 格式 ( busybox 的fstab文件里)

                 #device  mount-point  type  options dump fsck order

                 例如 挂载上边的proc 

                 proc /proc proc default 0 0
          2. 完善dev目录  创建设备文件

              udev机制  自动创建dev下的设备  busybox里使用的是mdev  为udev的简化版      


               使用方法如下 (源码里的文档)

               mkdir sys 

               在 fstab 里添加 sysfs  /sys  sysfs default 0 0

                                tmpfs  /dev   tmpfs  default 0 0    

               然后

                   在rcS里添加   mkdir /dev/pts 

                                  mount -t devpts devpts /dev/pts 

                                  echo /sbin/mdev > /proc/sys/kernel/hotplug

                                  mdev -s

网络挂接根文件系统 

            1. 服务器启动nfs服务
                配置文件 虚拟机 /etc/exports中添加 要被挂接的目录

                如
                   /work/nfs_root/my_fs

             2.重启nfs服务 


               挂接命令  本机挂接 sudo mount -t nfs 10.20.17.2 :/work/nfs_root/my_fs  /mnt              

             3.开发板手动挂接 (启动开发板后挂接)

                     mkdir  /mnt

                     mount -t nfs -o nolock 10.20.17.2 :/work/nfs_root/my_fs  /mnt 

             4.改uboot命令行参数从NFS启动

                     格式如下   参考 内核文档 /Document/nfsroot.TXT           

                     bootargs=noinitrd root=/dev/nfs nfsroot=10.20.17.2:/work/nfs_root/my_fs  ip=10.20.17.1:10.20.17.2:255.255.255.254:255.255.255.0::eth3::off  init=/linuxrc console=ttySAC0  

制作镜像文件
  1. 制作yaffs映象 文件(这里指的是yaffs2)

      /work/system/ yaffs_source_util_larger_small_page_nand.tar.bz2支持制作yaffs 和yaffs2 的镜像文件

      解压该文件 

       进入  Development_util_ok/yaffs2/utils 执行 make 得到 
    book@book-desktop:/work/system/Development_util_ok/yaffs2/utils$ ls 
     Makefile         mkyaffsimage.c  yaffs_ecc.o          yaffs_tagsvalidity.c
     mkyaffs2image    mkyaffsimage.o  yaffs_packedtags1.c  yaffs_tagsvalidity.o
     mkyaffs2image.c  nand_ecc.c      yaffs_packedtags1.o
     mkyaffs2image.o  nand_ecc.o      yaffs_packedtags2.c
     mkyaffsimage     yaffs_ecc.c     yaffs_packedtags2.o
   book@book-desktop:/work/system/Development_util_ok/yaffs2/utils$

      其中 mkyaffs2imag 用于制作yaffs2 镜像

     将其cp到 /usr/local/bin/目录中  并chmod +x  mkyaffs2imag

     回到 /work/nfs_root

     执行 mkyaffs2imag my_fs  my_fs_yaffs2 即可的得到根文件的镜像文件

     烧写到 flash 即可 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章