• Linux内核启动---init进程


     一.  Linux内核启动

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

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

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

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

    1.  init进程

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

    1. 928 static int __ref kernel_init(void *unused)
    2. 929 {
    3. 930 int ret;
    4. 931
    5. 932 kernel_init_freeable(); /* init 进程的一些其他初始化工作 */
    6. 933 /* need to finish all async __init code before freeing the
    7. memory */
    8. 934 async_synchronize_full(); /* 等待所有的异步调用执行完成 */
    9. 935 free_initmem(); /* 释放 init 段内存 */
    10. 936 mark_rodata_ro();
    11. 937 system_state = SYSTEM_RUNNING; /* 标记系统正在运行 */
    12. 938 numa_default_policy();
    13. 939
    14. 940 flush_delayed_fput();
    15. 941
    16. 942 if (ramdisk_execute_command) {
    17. 943 ret = run_init_process(ramdisk_execute_command);
    18. 944 if (!ret)
    19. 945 return 0;
    20. 946 pr_err("Failed to execute %s (error %d)\n",
    21. 947 ramdisk_execute_command, ret);
    22. 948 }
    23. 949
    24. 950 /*
    25. 951 * We try each of these until one succeeds.
    26. 952 *
    27. 953 * The Bourne shell can be used instead of init if we are
    28. 954 * trying to recover a really broken machine.
    29. 955 */
    30. 956 if (execute_command) {
    31. 957 ret = run_init_process(execute_command);
    32. 958 if (!ret)
    33. 959 return 0;
    34. 960 panic("Requested init %s failed (error %d).",
    35. 961 execute_command, ret);
    36. 962 }
    37. 963 if (!try_to_run_init_process("/sbin/init") ||
    38. 964 !try_to_run_init_process("/etc/init") ||
    39. 965 !try_to_run_init_process("/bin/init") ||
    40. 966 !try_to_run_init_process("/bin/sh"))
    41. 967 return 0;
    42. 968
    43. 969 panic("No working init found. Try passing init= option to
    44. kernel. "
    45. 970 "See Linux Documentation/init.txt for guidance.");
    46. 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 中,缩减后的函数内容如下:

    1. 973 static noinline void __init kernel_init_freeable(void)
    2. 974 {
    3. 975 /*
    4. 976 * Wait until kthreadd is all set-up.
    5. 977 */
    6. 978 wait_for_completion(&kthreadd_done);/* 等待 kthreadd 进程准备就绪 */
    7. ......
    8. 998
    9. 999 smp_init(); /* SMP 初始化 */
    10. 1000 sched_init_smp(); /* 多核(SMP)调度初始化 */
    11. 1001
    12. 1002 do_basic_setup(); /* 设备初始化都在此函数中完成 */
    13. 1003
    14. 1004 /* Open the /dev/console on the rootfs, this should never fail */
    15. 1005 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) <
    16. 0)
    17. 1006 pr_err("Warning: unable to open an initial console.\n");
    18. 1007
    19. 1008 (void) sys_dup(0);
    20. 1009 (void) sys_dup(0);
    21. 1010 /*
    22. 1011 * check if there is an early userspace init. If yes, let it do
    23. 1012 * all the work
    24. 1013 */
    25. 1014
    26. 1015 if (!ramdisk_execute_command)
    27. 1016 ramdisk_execute_command = "/init";
    28. 1017
    29. 1018 if (sys_access((const char __user *) ramdisk_execute_command,
    30. 0) != 0) {
    31. 1019 ramdisk_execute_command = NULL;
    32. 1020 prepare_namespace();
    33. 1021 }
    34. 1022
    35. 1023 /*
    36. 1024 * Ok, we have completed the initial bootup, and
    37. 1025 * we're essentially up and running. Get rid of the
    38. 1026 * initmem segments and start the user-mode stuff..
    39. 1027 *
    40. 1028 * rootfs is available now, try loading the public keys
    41. 1029 * and default modules
    42. 1030 */
    43. 1031
    44. 1032 integrity_load_keys();
    45. 1033 load_default_modules();
    46. 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 程序,以此来进去用户态。

  • 相关阅读:
    filament沙盒传参数到shader
    【2023云栖】陈守元:阿里云开源大数据产品年度发布
    重磅来袭!MoneyPrinterPlus一键发布短视频到视频号,抖音,快手,小红书上线了
    Ansible之 AWX 管理清单和凭据的一些笔记
    基于卷积神经网络与双向长短时融合的锂离子电池剩余使用寿命预测
    upload-labs靶场未知后缀名解析漏洞
    SSM+基于SSM的课堂考勤管理系统的设计与实现 毕业设计-附源码191617
    【SEC 学习】注册表添加启动脚本
    【Unity3D】UI Toolkit元素
    从0开始学习JavaScript--JavaScript 流程控制
  • 原文地址:https://blog.csdn.net/wojiaxiaohuang2014/article/details/133306784