嵌入式根文件系統分析

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 即可 

 

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