第4阶段——制作根文件系统之分析init_post()如何启动第1个程序(1)

本章学习如何启动第一个应用程序

1.在前面的分析中我们了解到,在init进程中内核挂接到根文件系统之后,会开始启动第一个应用程序:

kernel_init函数代码如下:

static int __init kernel_init(void * unused)    //进入init进程                                  
{    
   prepare_namespace()  //挂载根文件系统
   {
     ... ...      / /通过解析出来的命令行参数” root=/dev/mtdblock3”来挂接根文件系统 mount_root();   //开始挂载
   }

   init_post();           //启动应用程序     
}
}

2.接下来开始分析init_post()如何启动应用程序的,代码如下:

static int noinline init_post(void)
{
  /*内核已经初始化完成,所以清除__init_begin段到__init_end段之间的数据*/
       free_initmem();
       unlock_kernel();
       mark_rodata_ro();
       system_state = SYSTEM_RUNNING;
       numa_default_policy(); 

/*  打开dev/console控制台设备(串口0),使用户能输入信息, /dev/console即成为kernel_init进程的标准输入源(文件描述符0),   打开失败则打印Warning: unable to open an initial console.\n   */
       if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
              printk(KERN_WARNING "Warning: unable to open an initial console.\n");

当我们删除根文件系统的内容再启动内核,发现串口就会打印上面的字符串,如下图:

会显示打开dev/console失败,是因为根文件系统还是在root=/dev/mtdblock3, 所以能挂载根文件系统,我们擦除了mtd3内容,也就是dev里面的内容,所以无法打开console控制台。 

接下来继续分析init_post():

/*调用dup打开/dev/console文件描述符两次, 该控制台设备就也可以供标准输出和标准错误使用(文件描述符1和2),kernel_init进程现在就拥有3个文件描述符--标准输入、标准输出以及标准错误。*/
    (void) sys_dup(0);
    (void) sys_dup(0);
     if (ramdisk_execute_command) {       //若 ramdisk_execute_command为0,不运行它
         run_init_process(ramdisk_execute_command);  
         printk(KERN_WARNING "Failed to execute %s\n",
          ramdisk_execute_command); }

搜索上面ramdisk_execute_command,发现它是一个char型全局数组,找到它被用在init_setup()中,代码如下:

static int __init rdinit_setup(char *str)
{
         unsigned int i;
  
   /* 使ramdisk_execute_command数组等于str  *、
         ramdisk_execute_command = str;     

         /* See "auto" comment in init_setup */
         for (i = 1; i < MAX_INIT_ARGS; i++)
                   argv_init[i] = NULL;
         return 1;
}

__setup("rdinit=", rdinit_setup);

ramdisk_execute_command

发现上面__setup和我们上节分析的挂载根文件系统的__setup都是一样的

它是匹配命令行中以” rdinit=”开头的字符串,由于我们uboot的命令行参数中没有”rdint=”,所以ramdisk_execute_command=0,不执行if判断

接下来继续分析init_post():

if (execute_command) {   // execute_command不为0, 运行它

/* run_init_process 运行目标程序成功后会一直死循环*/          
run_init_process(execute_command);  

/*run_init_process运行失败退出后,打印Failed to execute /linuxrc.  Attempting defaults...  */
              printk(KERN_WARNING "Failed to execute %s.  Attempting "
                                   "defaults...\n", execute_command);
                      }

搜索上面execute_command,发现它是一个char型全局数组,找到它被用在init_setup()中,代码如下:

static int __init init_setup(char *str)
{
       unsigned int i;
 
     /*execute_command =str*/
       execute_command = str;     
       for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
       return 1;
}
__setup("init=", init_setup);

execute_command

发现上面__setup和我们上节分析的挂载根文件系统的__setup都是一样的

显然这里就是用来匹配命令行中以” init=”开头的字符串,然后再将命令行参数bootargs中的” init=/linuxrc”中的” /linuxrc”放在execute_command数组中.

(init=/linuxrc:指定内核启动后运行的第一个脚本是当前目录下linuxrc脚本)     

最终__setup("init=", init_setup)宏= { __setup_str_ root_dev_setup[], root_dev_setup , 0 };

然后放在.init.setup段中,在内核启动后进入start_kernel()函数中使用这个宏,并将” /linuxrc”放在execute_command数组中.

当文件系统被擦除后,就会运行linuxrc应用程序失败,打印执行linuxrc失败,如下图:

接下来继续分析init_post():

/*运行应用程序失败后,从下面3个地方查找可能出现 init应用程序的所有地方*/
       run_init_process("/sbin/init");
       run_init_process("/etc/init");
       run_init_process("/bin/init");

 

/*试图建立/bin/sh 来代替应用程序 */
       run_init_process("/bin/sh");


  /*如上图所示,当前面的所有情况都失败时,调用panic。这样内核就会试图同步磁盘,确保其状态一致。     如果超过了内核选项中定义的时间,它也可能会重新启动机器。*/
       panic("No init found.  Try passing init= option to kernel.");

}

在这里init_post函数就分析完毕了.

3.当在内核中,能输入数据时,表示根文件系统的应用程序启动完毕

比如输入ps查看进程,如下图,(ps-process status)

接下来开始分析init进程,知道命令是怎么来的

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏灯塔大数据

Python-解决Cx_Oracle查询时UnicodeDecodeError的问题

近期在项目中,要对1张100多万条记录的表进行查询,然后进行一些数据的统计,但是在这个过程中,发现只查询出来几条数据就出现了UnicodeDecodeErro...

2726
来自专栏Java学习123

Python操作文件目录

2816
来自专栏PHP在线

LINUX软链接和硬链接

1.Linux链接概念 Linux链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link)。默认情况下,ln命令产...

3205
来自专栏黑白安全

Sqlmap的使用

Sqlmap是开源的自动化SQL注入工具,由Python(2)写成,具有如下特点:

1093
来自专栏Linux运维学习之路

Nginx的各种报错总结

1、Nginx安装过程报错 错误一:软件依赖包未正确安装问题---PCRE依赖包没有安装 ./configure: error: the HTTP ...

21310
来自专栏梧雨北辰的开发录

Swift开发: 常见问题汇总

1、Unknown class xxClass in Interface Builder file. 这个问题出现在使用Xib时设置自定义类名的时候。在参考OC...

2758
来自专栏王亚昌的专栏

分享一个调用耗时统计类

    项目中经常需要统计调用一个外部接口的耗时,在做性能测试时也常需要分析N次调用所需时间,这类统计有一个共性点,即关注调用或执行的相对时间,而不关心绝对时间...

792
来自专栏电光石火

ThinkPHP导出CSV、Excel

如题,我们在实际中EXCEL的导出比导入用的多,这里给大家分享一个导出CSV EXCEL导出,简单,方便。 首先我们在Thinkphp/Library...

2198
来自专栏精讲JAVA

IDEA 代码生成插件 CodeMaker

Java 开发过程中经常会遇到编写重复代码的事情,例如说:编写领域类和持久类的时候,大部分时候它们的变量名称,类型是一样的,在编写领域类的时候常常要重复写类似的...

642
来自专栏王小雷

程序员必知的LinuxShell命令

程序员必知的LinuxShell命令 grep (Globle Regular Expression Print全局正则表达式) 命令是一种强大的文本搜索工具,...

1857

扫码关注云+社区