Linux内核启动---init进程

 一.  Linux内核启动

我们接下来重点看一下 init 进程,kernel_init 就是 init 进程的进程函数。

本文继上一篇文章的学习,地址如下:

Linux内核启动流程-第二阶段rest_init函数_凌肖战的博客-CSDN博客

二.  Linux内核启动---init进程

1.  init进程

kernel_init 函数就是 init 进程具体做的工作,定义在文件 init/main.c 中,函数内容如下:

928 static int __ref kernel_init(void *unused)
929 {
930 int ret;
931
932 kernel_init_freeable(); /* init 进程的一些其他初始化工作 */
933 /* need to finish all async __init code before freeing the 
memory */
934 async_synchronize_full(); /* 等待所有的异步调用执行完成 */
935 free_initmem(); /* 释放 init 段内存 */
936 mark_rodata_ro();
937 system_state = SYSTEM_RUNNING; /* 标记系统正在运行 */
938 numa_default_policy();
939
940 flush_delayed_fput();
941
942 if (ramdisk_execute_command) {
943 ret = run_init_process(ramdisk_execute_command);
944 if (!ret)
945 return 0;
946 pr_err("Failed to execute %s (error %d)\n",
947 ramdisk_execute_command, ret);
948 }
949
950 /*
951 * We try each of these until one succeeds.
952 *
953 * The Bourne shell can be used instead of init if we are
954 * trying to recover a really broken machine.
955 */
956 if (execute_command) {
957 ret = run_init_process(execute_command);
958 if (!ret)
959 return 0;
960 panic("Requested init %s failed (error %d).",
961 execute_command, ret);
962 }
963 if (!try_to_run_init_process("/sbin/init") ||
964 !try_to_run_init_process("/etc/init") ||
965 !try_to_run_init_process("/bin/init") ||
966 !try_to_run_init_process("/bin/sh"))
967 return 0;
968
969 panic("No working init found. Try passing init= option to 
kernel. "
970 "See Linux Documentation/init.txt for guidance.");
971 }

第 932 行,kernel_init_freeable 函数用于完成 init 进程的一些其他初始化工作,稍后再来具体看一下此函数。

第 940 行,ramdisk_execute_command 是一个全局的 char 指针变量,此变量值为“/init”,也就是根目录下的 init 程序。ramdisk_execute_command 也可以通过 uboot 传递,在 bootargs 中使用“rdinit=xxx”即可,xxx 为具体的 init 程序名字。

第 943 行,如果存在“/init”程序的话就通过函数 run_init_process 来运行此程序。

第 956 行,如果 ramdisk_execute_command 为空的话就看 execute_command 是否为空,反 正不管如何一定要在根文件系统中找到一个可运行的 init 程序。execute_command 的值是通过 uboot 传递,在 bootargs 中使用“init=xxxx”就可以了,比如“init=/linuxrc”表示根文件系统中 的linuxrc 就是要执行的用户空间 init 程序。

第 963~966 行,如果 ramdisk_execute_command 和 execute_command 都为空,那么就依次

查找“/sbin/init”、“/etc/init”、“/bin/init”和“/bin/sh”,这四个相当于备用 init 程序,如果这四

个也不存在,那么 Linux 启动失败!

第 969 行,如果以上步骤都没有找到用户空间的 init 程序,那么就提示错误发生!

2.  kernel_init_freeable 函数

最后来简单看一下 kernel_init_freeable 函数,前面说了,kernel_init 会调用此函数来做一些 init 进程初始化工作。kernel_init_freeable函数 定义在文件 init/main.c 中,缩减后的函数内容如下:

973 static noinline void __init kernel_init_freeable(void)
974 {
975 /*
976 * Wait until kthreadd is all set-up.
977 */
978 wait_for_completion(&kthreadd_done);/* 等待 kthreadd 进程准备就绪 */
......
998 
999 smp_init(); /* SMP 初始化 */
1000 sched_init_smp(); /* 多核(SMP)调度初始化 */
1001
1002 do_basic_setup(); /* 设备初始化都在此函数中完成 */
1003
1004 /* Open the /dev/console on the rootfs, this should never fail */
1005 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) <
0)
1006 pr_err("Warning: unable to open an initial console.\n");
1007
1008 (void) sys_dup(0);
1009 (void) sys_dup(0);
1010 /*
1011 * check if there is an early userspace init. If yes, let it do 
1012 * all the work
1013 */
1014
1015 if (!ramdisk_execute_command)
1016 ramdisk_execute_command = "/init";
1017
1018 if (sys_access((const char __user *) ramdisk_execute_command, 
0) != 0) {
1019 ramdisk_execute_command = NULL;
1020 prepare_namespace();
1021 }
1022
1023 /*
1024 * Ok, we have completed the initial bootup, and
1025 * we're essentially up and running. Get rid of the
1026 * initmem segments and start the user-mode stuff..
1027 *
1028 * rootfs is available now, try loading the public keys
1029 * and default modules
1030 */
1031
1032 integrity_load_keys();
1033 load_default_modules();
1034 }

1002 行, do_basic_setup 函数用于完成 Linux 下设备驱动初始化工作!非常重要。
do_basic_setup 会调用 driver_init 函数完成 Linux 下驱动模型子系统的初始化。

1005 行,打开设备“ /dev/console ”,在 Linux 中一切皆为文件!因此“ /dev/console ”也是一个文件,此文件为控制台设备。每个文件都有一个文件描述符,此处打开的“ /dev/console 文件描述符为 0 ,作为标准输入 (0)
1008 1009 行, sys_dup 函数将标准输入 (0) 的文件描述符复制了 2 次,一个作为标准 (1) ,一个作为标准错误 (2) 。这样标准输入、输出、错误都是 /dev/console 了。
console 通过 uboot bootargs 环境变量设置,“ console=ttymxc0,115200 ”表示将 /dev/ttymxc0 设置为 console 也就是 I.MX6U 的串口 1 。当然,也可以设置其他的设备为 console ,比如虚拟控制台 tty1 ,设 tty1 console 就可以在 LCD 屏幕上看到系统的提示信息。

1020 行,调用函数 prepare_namespace 来挂载根文件系统。根文件系统也是由命令行参
数指定的,就是 uboot bootargs 环境变量。比如“ root=/dev/mmcblk1p2 rootwait rw ”就表示根
文件系统在 /dev/mmcblk1p2 中,也就是 EMMC 的分区 2 中。

Linux 内核启动流程就分析到这里,Linux 内核最终是需要和根文件系统打交道的,需要挂载根文件系统,并且执行根文件系统中的 init 程序,以此来进去用户态。

猜你喜欢

转载自blog.csdn.net/wojiaxiaohuang2014/article/details/133306784