u-boot、kernel和filesystem 執行過程分析

 

標題:linux

  Uboot -kerne-root 啓動流程shell

內容:vim

  ※uboot啓動流程bash

  ※Kernel啓動流程網絡

  ※Root啓動流程app

  ※構建根文件系統函數

 

/*********************************oop

*u-boot:   u-boot2012.04.01post

*kernel:    linux-2.6.22ui

*busybox:    busybox-1.1.6

*********************************/ 

 

 

 

 

1、uboot啓動流程

1.初始化硬件

2.把內核從NAND讀到SDRAM

3.設置內核啓動參數

4.跳轉執行內核

 

執行第一個代碼:/cpu/arm920t/start.S

 

 

第一階段(start.S)

reset:

   set the cpu to SVC32 mode          //管理模式

   turn off the watchdog               //關看門狗

   mask all IRQs                         //屏蔽中斷

   cpu_init_crit

  flush v4 I/D caches              //關閉caches

  disable MMU stuff and caches     //關閉MMU

  lowlevel_init  //配置SDRAM

   Relocate                          //重定位

   stack_setup:                       //設置堆棧

   clear_bss:                         //清楚bss段

   start_armboot                      //跳轉執行第二階段

 

第二階段(board.c)

start_armboot (void)

  init_sequence

  cpu_init,                      /* basic cpu dependent setup */

  board_init,                   /* basic board dependent setup */

  interrupt_init,              /* set up exceptions */

  env_init,                      /* initialize environment */

  init_baudrate,               /* initialze baudrate settings */

  serial_init,                   /* serial communications setup */

  console_init_f,             /* stage 1 init of console */

  display_banner,                        /* say that we are here */

  flash_init            初始化NOR

  nand_init()           初始化NAND

  env_relocate ()        將環境變量讀入指定位置

  cs8900_get_enetaddr     初始化網絡設備

  main_loop ()            死循環

    s = getenv ("bootdelay")

      bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    s = getenv ("bootcmd");

    Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0  //環境變量

    run_command (s, 0);

    continue;

 

 

Uboot命令的實現

①串口輸入命令(字符串)

②動做(函數),命令對應於名字

於是根據命令找到對應函數,來執行。Uboot裏面有一個結構體

包含1.名

2.函數

struct cmd_tbl_s {

          char                  *name;              /* Command Name                                 */

          int                    maxargs;           /* maximum number of arguments           */

          int                    repeatable;         /* autorepeat allowed?                */

                                                          /* Implementation function         */

          int                    (*cmd)(struct cmd_tbl_s *, int, int, char *[]);

          char                  *usage;              /* Usage message           (short)   */

#ifdef  CFG_LONGHELP

          char                  *help;               /* Help  message            (long)   */

#endif

#ifdef CONFIG_AUTO_COMPLETE

          /* do auto completion on the arguments */

          int                    (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);

#endif

};

 

此時run_command函數根據名字匹配該結構體

 

Run_command

命令的提取

while (*str) {

 

                      /*

                       * Find separator, or string end

                       * Allow simple escape of ';' by writing "\;"

                       */

                      for (inquotes = 0, sep = str; *sep; sep++) {

                                  if ((*sep=='\'') &&

                                      (*(sep-1) != '\\'))

                                              inquotes=!inquotes;

 

                                  if (!inquotes &&

                                      (*sep == ';') &&         /* separator                    */

                                      ( sep != str) &&         /* past string start           */

                                      (*(sep-1) != '\\'))        /* and NOT escaped        */

                                              break;

                      }

parse_line()   //解析命令

Cmdtp=find_cmd()      //匹配命令  cmdtp就是指向上面的結構體cmd_tbl_s

for (cmdtp = &__u_boot_cmd_start;

               cmdtp != &__u_boot_cmd_end;

               cmdtp++)

 

 

 

 

以上:

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

好比啓動命令:

Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0以bootm 0x30007fc0爲例

U_BOOT_CMD(

     bootm,  CFG_MAXARGS,         1,         do_bootm,

     "bootm   - boot application image from memory\n",

     "[addr [arg ...]]\n    - boot application image stored in memory\n"

     "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"

     "\t'arg' can be the address of an initrd image\n"

#ifdef CONFIG_OF_FLAT_TREE

     "\tWhen booting a Linux kernel which requires a flat device-tree\n"

     "\ta third argument is required which is the address of the of the\n"

     "\tdevice-tree blob. To boot that kernel without an initrd image,\n"

     "\tuse a '-' for the second argument. If you do not pass a third\n"

     "\ta bd_info struct will be passed instead\n"

#endif

);

U_boot_cmd的定義:

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

以上能夠展開爲;

cmd_tbl_t __u_boot_cmd_bootm  __attribute__ ((unused,section (".u_boot_cmd")))

= {bootm, CFG_MAXARGS, 1, do_bootm, "bootm   - boot application image from memory\n", help}

以上解釋爲:bootm命令定義了__u_boot_cmd_bootm結構體,該結構體類型是cmd_tbl_t,而且強制爲.u_boot_cmd段屬性

 

Uboot啓動內核

內核啓動依賴nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0

nand read.jffs2 0x30007fc0 kernel把內核讀到0x30007fc0

從哪裏讀? --kernel分區在配置文件裏面寫死了smdk2410.h

讀到哪裏? --0x30007fc0

Nand命令分析:

"nand read[.jffs2]     - addr off|partition size\n"

 

bootm 0x30007fc0從bootm 0x30007fc0啓動它

Bootm命令分析:

do_bootm

image_header_t *hdr = &header;

typedef struct image_header {

   uint32_t            ih_magic;          /* Image Header Magic Number  */

   uint32_t            ih_hcrc; /* Image Header CRC Checksum */

   uint32_t            ih_time;            /* Image Creation Timestamp      */

   uint32_t            ih_size; /* Image Data Size                     */

   uint32_t            ih_load; /* Data  Load  Address               */

   uint32_t            ih_ep;               /* Entry Point Address                */

   uint32_t            ih_dcrc; /* Image Data CRC Checksum     */

   uint8_t              ih_os;                /* Operating System                   */

   uint8_t              ih_arch; /* CPU architecture                    */

   uint8_t              ih_type; /* Image Type                            */

   uint8_t              ih_comp;           /* Compression Type                  */

   uint8_t              ih_name[IH_NMLEN];   /* Image Name              */

} image_header_t;

memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);

//若是當前地址不是入口地址,移到加載地址0x30008000,因爲頭部佔用64字節,因此此時地址是0x30007fc0

do_bootm_linux  (cmdtp, flag, argc, argv,

                                       addr, len_ptr, verify); //啓動內核

setup_start_tag (bd);

setup_memory_tags (bd);

setup_commandline_tag (bd, commandline);

setup_end_tag (bd);

printf ("\nStarting kernel ...\n\n");

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

 

 

 

2、Kernel啓動流程

 

處理uboot傳入的參數

①判斷是否支持這個CPU

②判斷是否支持這個單板

③創建頁表

④使能MMU

⑤跳到start_kernel函數

 

內核執行的第一個代碼文件:/arch/arm/kernel/head.S  和 /arch/arm/boot/bootp/init.S

 

Start_kernel分析

1.輸出內核版本信息

2.Setup_arch($command_line)

3.Setup_command_line(command_line)

 

 

調用關係:

Start_kernel

            printk(linux_banner);   //輸出版本信息

            setup_arch(&command_line); //匹配ID,arch-type

                        setup_processor();

                                    list = lookup_processor_type(processor_id);

                        mdesc = setup_machine(machine_arch_type);

parse_cmdline(cmdline_p, from); //解析命令

            setup_command_line(command_line);

                        strcpy (saved_command_line, boot_command_line);

Rest_init

  Kernel_thread()    //線程

  // kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

    kernel_init()

    Prepare_namespace()

    Mount_root()

    Init_post()

      Run_init_process()  //執行文件系統第一個程序

      ...

 

3、Root啓動流程

init過程

1.讀取配置文件

2.解析配置文件

3.執行客戶程序

 

 

配置文件:1.指定程序;2.什麼時候執行

 

busybox調用過程

busybox -> init_main

               parse_inittab

                           file = fopen(INITTAB, "r");//打開配置文件/etc/inittab

                          (沒有配置文件時使用默認配置文件)

                           new_init_action()

                           #1.建立init_action結構,填充

                           #2.把這個結構放入init_action_list鏈表裏面

               run_actions(SYSINIT);

                           waitfor(a, 0); //執行程序,等待執行完畢

                                       run(a); //系統調用,建立precess子進程

                                       waitpid(runpid, &status, 0) //等待結束

                           delete_init_action(a);//在init_action_list裏面刪掉應用程序

               run_actions(WAIT);

                           waitfor(a, 0);

                           delete_init_action(a);

               run_actions(ONCE);

                           run(a);//不會等待子進程結束

                           delete_init_action(a);//在init_action_list裏面刪

               while (1) {

                     run_actions(RESPAWN);

                                       run(a)  

                           run_actions(ASKFIRST);

                                       run(a)

                                                   打印:"\nPlease press Enter to activate this console. ";

                                                   等待回車

                                                   建立子進程

                           wpid = wait(NULL);//等待子進程退出

                           while (wpid > 0) {

                                       a->pid = 0;//退出後,設置pid=0

                           }

               }

              

             

 

 

init_action_list裏面有id,action,process等等

 

 

 static void new_init_action(int action, const char *command, const char *cons)

struct init_action *new_action, *a, *last;

 

init_action裏面有:

struct init_action *next;

   int action;

   pid_t pid;    //進程號

   char command[INIT_BUFFS_SIZE];  //應用程序

   char terminal[CONSOLE_NAME_SIZE];  //終端

 

 

new_init_action作了什麼

1.建立init_action結構,填充

2.把這個結構放入init_action_list鏈表裏面          

 

如今假設沒有配置文件,根據

/* 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, "");

              

來反推出默認配置項

#inittab格式:(文件在example/inittab文件)

 #<id>:<runlevels>:<action>:<process>

 

 #<id>         :/dev/id,最終用做終端:stdin,stdout,stderrer:printf,scanf,err

 #<runlevels>  :忽略

 #<action>     :什麼時候執行的

 <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,

                                  restart, ctrlaltdel, and shutdown.

 

 #<process>    :應用程序或者腳本

 

 

 

 new_init_action(ASKFIRST, -/bin/sh, /dev/tty2);    

 

 

 new_init_action(CTRLALTDEL, "reboot", "");

 id=null

 runlevels=null

 action=ctrlaltdel

 那麼上面就是:

 ::ctrlaltdel:reboot

 同理可知其餘的配置項是:

 ::shutdown:umount -a -r

 ::restart:init

 ::askfirst:-/bin/sh

 tty2:askfirst:-/bin/sh

 tty3:askfirst:-/bin/sh

 tty4:askfirst:-/bin/sh

 ::sysinit:/etc/init.d/rcS

 

 

 

 

 

 其實:

 main裏面

 signal(SIGHUP, exec_signal);

   signal(SIGQUIT, exec_signal);

   signal(SIGUSR1, shutdown_signal);

   signal(SIGUSR2, shutdown_signal);

   signal(SIGINT, ctrlaltdel_signal);

   signal(SIGTERM, shutdown_signal);

   signal(SIGCONT, cont_handler);

   signal(SIGSTOP, stop_handler);

   signal(SIGTSTP, stop_handler);

      

當用戶按下 ctrl + alt +del時會產生一個信號,而後執行ctrlaltdel_signal函數

static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)

{

   run_actions(CTRLALTDEL);

}

 

 

 

 

 

總結:應用程序所須要的文件

 

1./dev/console /dev/null

2.配置文件/etc/inittab

3.配置文件裏面指定的應用程序

4.庫文件

5.init自己,即busybox

以上就是最小根文件系統所須要的項

 

構建根文件系統

 

 

read  INSTALL  rirst.

 

 

 

 

總結:應用程序所須要的文件

 

1./dev/console /dev/null

2.配置文件/etc/inittab

3.配置文件裏面指定的應用程序

4.庫文件

5.init自己,即busybox

以上就是最小根文件系統所須要的項

 

 

編譯busybox

先閱讀install文件可知道如何編譯它

 

make menuconfig

make

make install(這裏安裝要注意)

安裝到咱們指定的某個文件裏面

使用make CONFIG_PREFIX=/path/from/root install

 

 

 

編譯須要在makefile裏面加上cross_compiler裏面加上arm-linux-

 

 

1、建立console

 

 

如今上面編譯後busybox目錄是work/FL_fs/first_fs

在此目錄下:ls /dev/console /dev/null -l

crw------- 1 root root 5, 1 2015-01-05 20:57 /dev/console

crw-rw-rw- 1 root root 1, 3 2015-01-05 20:30 /dev/null

那麼根據它來建立console null等設備

 

#mkdir dev

#cd dev

#mknod console c 5 1

#mknod null c 1 3

#ls -l

顯示:

 crw-r--r-- 1 root root 5, 1 2015-05-06 20:39 console

crw-r--r-- 1 root root 1, 3 2015-05-06 20:40 null

表示建立成功

 

 

 

2、配置項

#cd work/FL_fs/first_fs

#mkdir etc

#vim  etc/inittab

 

輸入:

console::askfirst:-/bin/sh

 

 

3、安裝c庫

#cd lib

#cp /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib *.so* /work/FL_fs/first_fs/lib -d

 

 

 

 end

 

 最小根文件系統已經完成。

 

 此時須要製做鏡像文件以燒寫到硬件。

 yaffs2和jffs文件系統

 

 1.製做yaffs2

 #cd work/system

 yaffs_source_util_large_smmall_page_nand.tar.bz2

 

 #tar xjf  yaffs_source_util_large_small_page_nand.tar.bz2

 #cd Developement_util_ok

 #cd yaffs2

 #cd utils

 #make

 #cp mkyaffs2image /usr/local/bin

 #chmod +x /usr/local/bin/mkyaffs2image

 

 

 

 #cd /work/FL_fs

 #mkyaffs2image first_fs first_fs.yaffs2

 

 此後燒寫到開發板裏面。

 能夠啓動,並能使用ls,cd等命令。

 

 

 

 如何改進?

 

 #cd ../first_fs

 #mkdir proc

 

 

 單板上有虛擬系統

 #mkdir proc

 #mount -t proc none /proc

 #ps

 能夠看到哪些程序及其內容

   PID TTY          TIME CMD

  383 pts/2    00:00:00 ps

30564 pts/2    00:00:00 su

30573 pts/2    00:00:00 bash

383 進程號

#cd 383

若是不想 手動掛載 #mount -t proc none /proc

也能夠寫到配置文件裏面

 

#vim inittab

++ ::sysinit:/etc/init.d/rcS  須要腳本

#mkdir etc/init.d

#vim rcS

##mount -t proc none /proc

#chmod +x /etc/init.d/rcS

就能夠了

 

上面#mount -t proc none /proc

也可使用#mount -a

 

#mount -a是什麼意思?

讀出/etc/fstab內容來掛載

 

怎麼使用呢?

#mount -a會依賴fstab文件

fstab文件內容有哪些呢?

#device         mount-point    type     options     dump   fsck

proc        /proc        proc defaults    0       0

 

那麼,在/etc/fstab裏面輸入:

proc   /proc    default 0 0

而後在腳本rcS裏面使用#mount -a

 

 

而後再製做yaffs鏡像文件就和第一種方法是同樣的。

可使用#cat /proc/mounts查看掛載了哪些文件系統

 

 

繼續

 

 

上面的dev是手工建立

那麼怎麼自動建立dev下面的設備節點呢

方法:使用mdev

 

怎麼使用mdev?  在/

能夠查看mdev.txt

 

[1] mount -t sysfs sysfs /sys

[2] echo /bin/mdev > /proc/sys/kernel/hotplug

[3] mdev -s

[4] mount -t tmpfs mdev /dev

[5] mkdir /dev/pts

[6] mount -t devpts devpts /dev/pts

這6步就能夠了。

 

 

因此:

一、#cd ../first_fs

#mkdir sys

[1] mount -t sysfs sysfs /sys

[4] mount -t tmpfs mdev /dev

應該怎麼作?

#vim /etc/fstab

#device         mount-point    type     options     dump   fsck

proc        /proc        proc defaults    0        0

sysfs           /sys           sysfs    default     0      0

tmpfs           /dev           tmpfs    default     0      0

 

 

[2] echo /bin/mdev > /proc/sys/kernel/hotplug

[3] mdev -s

[5] mkdir /dev/pts

[6] mount -t devpts devpts /dev/pts

怎麼作呢?

#cd /etc/init.d/rcS

mount -a

mkdir /dev/pts/

mount -t devpts devpts /dev/pts

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

mdev -s

相關文章
相關標籤/搜索