根文件系统的构建与分析(一)之流程分析

根文件系统的构建与分析(一)

转载请注明 http://blog.csdn.net/jianchi88   Author:Lotte   邮箱:baihaowen08@126.com

此系列,我想从初学者的角度来体验怎样构建根文件系统,显然,我也是个初学者。

根文件系统第一个进程init到启动用户应用程序的过程:

qt应用程序也做了,u-boot、内核也移植了,那启动内核后,怎样才能运行qt应用程序呢?内核启动后,操作系统做了些什么?

 U-Boot    目的  ――→  启动内核

      |

     ↓

  内核    目的     ――→  启动第一个进程init(放在根文件系统上)     

      |

     ↓

根文件系统

      |

     ↓

用户应用程序

 

在内核初始化收尾阶段(/kernel-2.6.30.4/init/main.c)流程,start_kernel → rest_init → kernel_init → init_post。

下面的代码是内核启动最后阶段执行的init_post函数:

[cpp] view plain?
  1. static noinline int init_post(void)  
  2.     __releases(kernel_lock)  
  3. {  
  4.     /* need to finish all async __init code before freeing the memory */  
  5.     async_synchronize_full();  
  6.     free_initmem();  
  7.     unlock_kernel();  
  8.     mark_rodata_ro();  
  9.     system_state = SYSTEM_RUNNING;  
  10.     numa_default_policy();  
  11.   
  12.     if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  
  13.         printk(KERN_WARNING "Warning: unable to open an initial console. ");  
  14.   
  15.     (void) sys_dup(0);  
  16.     (void) sys_dup(0);  
  17.   
  18.     current->signal->flags |= SIGNAL_UNKILLABLE;  
  19.   
  20.     if (ramdisk_execute_command) {  
  21.         run_init_process(ramdisk_execute_command);  
  22.         printk(KERN_WARNING "Failed to execute %s ",  
  23.                 ramdisk_execute_command);  
  24.     }  
  25.   
  26.     /* 
  27.      * We try each of these until one succeeds. 
  28.      * 
  29.      * The Bourne shell can be used instead of init if we are 
  30.      * trying to recover a really broken machine. 
  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.     run_init_process("/sbin/init");  
  38.     run_init_process("/etc/init");  
  39.     run_init_process("/bin/init");  
  40.     run_init_process("/bin/sh");  
  41.   
  42.     panic("No init found.  Try passing init= option to kernel.");  
  43. }  


        在启动第一个程序前,首先打开控制台设备sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0),这样init进程就拥有一个控制台,并可以从中读取输入信息在串口终端显示,也可以向其中写入信息。

        在bootloader里设置传给内核的参数init=/linuxrc,execute_command就等于/linuxrc,如果定义了,就会run_init_process(execute_command);执行这个应用程序,如果没有定义这个参数,就会往下走。执行

run_init_process("/sbin/init");

run_init_process("/etc/init");

run_init_process("/bin/init");

run_init_process("/bin/sh");

中的其中一个。另外要说明的是,run_init_process();一旦执行了init或者linuxrc,就不会返回,一直执行。init这个进程是所有进程的父进程,有了它,后面才能有shell、qt等进程。

Init进程的功能:
• 它是所有其他进程的父进程。
• init进程根据文件的内容运行一系列程序和脚本文件,完成系统的各项配置,最终达到启动用户应用程序的目的。

下面我们来追随init的运行。

==================================      i am a line        ========================================

       当我们进入根文件系统,发现有很多命令,如ls、cp、rm等,这些都是应用程序,但这些都是通过链接到busybox实现的功能。如:

/bin/ls -> busybox

/bin/cp -> busybox

/bin/rm -> busybox

就连sbin/init进程也是链接到busybox的,所以busybox是很多命令程序和工具的集合。所以要分析init怎么运行,就要分析busybox。

解压busybox源码,在/init/init.c。(这个就是编译busybox后就是被init链接的代码)

函数调用流程:

busybox -> init_main() -> parse_inittab() -> run_actions()

解析inittab文件函数parse_inittab();

在parse_inittab()函数里,首先打开/etc/inittab这个文件,接着解析inittab。inittab配置文件的格式说明和例子在busybox的源码里面有,搜索一下就能找到。

----------------------------------------------------------------------------------------------------------------------

inittab配置文件的格式:

id:runlevel:action:process
• 其中:
–Id:用来指定所启动进程的控制台,在嵌入式系统中一般不添;
–Runlevel:busybox完全忽略runlevel字段;
–Action:指出init程序在执行相应process时,对process所采取的动作
 <action>: Valid actions include: sysinit, respawn, askfirst, wait, once, restart, ctrlaltdel, and shutdown.

–Process:具体的执行程序或者脚本;

-----------------------------------------------------------------------------------------------------------------------

        回到源码,在parse_inittab()函数里,为inittab文件作一些添油加醋的修饰后,最终调用new_init_action(),下面进行分析。

[cpp] view plain?
  1. static void parse_inittab(void)  
  2. {  
  3. #if ENABLE_FEATURE_USE_INITTAB  
  4.     char *token[4];  
  5.     parser_t *parser = config_open2("/etc/inittab", fopen_for_read);  
  6.   
  7.     if (parser == NULL)  
  8. #endif  
  9.     {  
  10.         /* No inittab file -- set up some default behavior */  
  11.         /* Reboot on Ctrl-Alt-Del */  
  12.         new_init_action(CTRLALTDEL, "reboot""");  
  13.         /* Umount all filesystems on halt/reboot */  
  14.         new_init_action(SHUTDOWN, "umount -a -r""");  
  15.         /* Swapoff on halt/reboot */  
  16.         if (ENABLE_SWAPONOFF)  
  17.             new_init_action(SHUTDOWN, "swapoff -a""");  
  18.         /* Prepare to restart init when a QUIT is received */  
  19.         new_init_action(RESTART, "init""");  
  20.         /* Askfirst shell on tty1-4 */  
  21.         new_init_action(ASKFIRST, bb_default_login_shell, "");  
  22. //TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users  
  23.         new_init_action(ASKFIRST, bb_default_login_shell, VC_2);  
  24.         new_init_action(ASKFIRST, bb_default_login_shell, VC_3);  
  25.         new_init_action(ASKFIRST, bb_default_login_shell, VC_4);  
  26.         /* sysinit */  
  27.         new_init_action(SYSINIT, INIT_SCRIPT, "");  
  28.         return;  
  29.     }  
  30.   
  31. #if ENABLE_FEATURE_USE_INITTAB  
  32.     /* optional_tty:ignored_runlevel:action:command 
  33.      * Delims are not to be collapsed and need exactly 4 tokens 
  34.      */  
  35.     while (config_read(parser, token, 4, 0, "#:",  
  36.                 PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) {  
  37.         /* order must correspond to SYSINIT..RESTART constants */  
  38.         static const char actions[] ALIGN1 =  
  39.             "sysinit""respawn""askfirst""wait""once"  
  40.             "ctrlaltdel""shutdown""restart";  
  41.         int action;  
  42.         char *tty = token[0];  
  43.   
  44.         if (!token[3]) /* less than 4 tokens */  
  45.             goto bad_entry;  
  46.         action = index_in_strings(actions, token[2]);  
  47.         if (action < 0 || !token[3][0]) /* token[3]: command */  
  48.             goto bad_entry;  
  49.         /* turn .*TTY -> /dev/TTY */  
  50.         if (tty[0]) {  
  51.             if (strncmp(tty, "/dev/", 5) == 0)  
  52.                 tty += 5;  
  53.             tty = concat_path_file("/dev/", tty);  
  54.         }  
  55.         new_init_action(1 << action, token[3], tty);  
  56.         if (tty[0])  
  57.             free(tty);  
  58.         continue;  
  59.  bad_entry:  
  60.         message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d",  
  61.                 parser->lineno);  
  62.     }  
  63.     config_close(parser);  
  64. #endif  
  65. }  

        可以看见,如果没配置文件inittab存在,它会默认一些配置。其中抽取一个来分析,如下:

[cpp] view plain?
  1. static void new_init_action(uint8_t action_type, const char *command, const char *cons)  
[cpp] view plain?
  1. new_init_action(ASKFIRST, bb_default_login_shell, VC_2);  

                                          bb_default_login_shell     ->          "-/bin/sh"         

                                                      VC_2                         ->          "/dev/tty2"

action_type对应inittab配置文件中Action字段

command对应inittab配置文件中Process字段

cons对应inittab配置文件中id字段

        new_init_action函数做了这些工作:

1、创建init_action结构,并填充。

2、把这个结构放入init_action_list链表

        假如我们没有配置文件inittab存在,它会默认一些配置,我们从默认的new_init_action反推出默认的配置文件inittab。

[cpp] view plain?
  1. if (parser == NULL)  
  2. #endif  
  3.     {  
  4.         /* No inittab file -- set up some default behavior */  
  5.         /* Reboot on Ctrl-Alt-Del */  
  6.         new_init_action(CTRLALTDEL, "reboot""");  
  7.         /* Umount all filesystems on halt/reboot */  
  8.         new_init_action(SHUTDOWN, "umount -a -r""");  
  9.         /* Swapoff on halt/reboot */  
  10.         if (ENABLE_SWAPONOFF)  
  11.             new_init_action(SHUTDOWN, "swapoff -a""");  
  12.         /* Prepare to restart init when a QUIT is received */  
  13.         new_init_action(RESTART, "init""");  
  14.         /* Askfirst shell on tty1-4 */  
  15.         new_init_action(ASKFIRST, bb_default_login_shell, "");  
  16. //TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users  
  17.         new_init_action(ASKFIRST, bb_default_login_shell, VC_2);  
  18.         new_init_action(ASKFIRST, bb_default_login_shell, VC_3);  
  19.         new_init_action(ASKFIRST, bb_default_login_shell, VC_4);  
  20.         /* sysinit */  
  21.         new_init_action(SYSINIT, INIT_SCRIPT, "");  
  22.         return;  
  23.     }  

根据这段代码写成inittab配置文件:

[cpp] view plain?
  1. #id:runlevel:action:process  
  2.   
  3. ::CTRLALTDEL:reboot  
  4. ::SHUTDOWN:umount -a -r  
  5. ::RESTART:init  
  6. ::ASKFIRST:-/bin/sh  
  7. tty2::ASKFIRST:-/bin/sh  
  8. tty3::ASKFIRST:-/bin/sh  
  9. tty4::ASKFIRST:-/bin/sh  
  10. ::SYSINIT:/etc/init.d/rcS  

        我们把这些包含执行时机、应用程序等结构体填充到init_action_list链表,这就完成了解析inittab文件函数parse_inittab(),在init_main()函数继续走下去,执行run_actions()。

[cpp] view plain?
  1. /* Now run everything that needs to be run */  
  2.   
  3.     /* First run the sysinit command */  
  4.     run_actions(SYSINIT);  
  5.   
  6.     /* Next run anything that wants to block */  
  7.     run_actions(WAIT);  
  8.   
  9.     /* Next run anything to be run only once */  
  10.     run_actions(ONCE);  
  11.   
  12.     /* Redefine SIGHUP to reread /etc/inittab */  
  13. #if ENABLE_FEATURE_USE_INITTAB  
  14.     signal(SIGHUP, reload_signal);  
  15. #else  
  16.     signal(SIGHUP, SIG_IGN);  
  17. #endif  
  18.   
  19.     /* Now run the looping stuff for the rest of forever */  
  20.     while (1) {  
  21.         /* run the respawn/askfirst stuff */  
  22.         run_actions(RESPAWN | ASKFIRST);  
  23.   
  24.         /* Don't consume all CPU time -- sleep a bit */  
  25.         sleep(1);  
  26.   
  27.         /* Wait for any child process to exit */  
  28.         wpid = wait(NULL);  
  29.         while (wpid > 0) {  
  30.             /* Find out who died and clean up their corpse */  
  31.             for (a = init_action_list; a; a = a->next) {  
  32.                 if (a->pid == wpid) {  
  33.                     /* Set the pid to 0 so that the process gets 
  34.                      * restarted by run_actions() */  
  35.                     a->pid = 0;  
  36.                     message(L_LOG, "process '%s' (pid %d) exited. "  
  37.                             "Scheduling for restart.",  
  38.                             a->command, wpid);  
  39.                 }  
  40.             }  
  41.             /* see if anyone else is waiting to be reaped */  
  42.             wpid = wait_any_nohang(NULL);  
  43.         }  
  44.     }  

        可见,最先执行是的Action字段里的sysinit这类,接着是wait,接着是once,最后是respawn和askfirst。在run_actions函数里,它提取init_action_list链表的项来用waitfor()执行应用程序或者脚本,等执行完毕,再用delete_init_action()删除链表中的该项。

小结,根文件系统要顺利执行用户应用程序需要:

1、/dev/console      /dev/null   当不设置inittab里id字段的话,标准输入、输出、错误会自动定位到/dev/null  

2、/etc/inittab

3、配置文件里指定的程序或者脚本

4、/lib下的动态库

5、init本身,即busybox

原文地址:https://www.cnblogs.com/alan666/p/8312474.html