天天看點

嵌入式根檔案系統分析

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

    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 時 連接配接檔案将會變成真實内容 照成根檔案系統過大     

至此最小根檔案系統作成