Android 10.0系统启动之init进程(一)-「Android取经之路」

[Android取经之路的源码都基于Android-Q(10.0) 进行分析]

Android init 启动进程主要分三个阶段分析:

  • 概述,Init如何被启动
  • Init进程启动的源码分析
  • 信号处理
  • rc语法分析
  • 启动服务
  • 属性服务

1.概述:

init进程是linux系统中用户空间的第一个进程,进程号为1.

当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。

init进程被赋予了很多重要工作,init进程启动主要分为两个阶段:

第一个阶段完成以下内容:

  • ueventd/watchdogd跳转及环境变量设置
  • 挂载文件系统并创建目录
  • 初始化日志输出、挂载分区设备
  • 启用SELinux安全策略
  • 开始第二阶段前的准备

第二个阶段完成以下内容:

  • 初始化属性系统
  • 执行SELinux第二阶段并恢复一些文件安全上下文
  • 新建epoll并初始化子进程终止信号处理函数
  • 设置其他系统属性并开启属性服务

2.架构

2.1 Init进程如何被启动?

Android 10.0系统启动之init进程(一)-「Android取经之路」

Init进程是在Kernel启动后,启动的第一个用户空间进程,PID为1。

kernel_init启动后,完成一些init的初始化操作,然后去系统根目录下依次找ramdisk_execute_command和execute_command设置的应用程序,如果这两个目录都找不到,就依次去根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动,只要这些应用程序有一个启动了,其他就不启动了。

Android系统一般会在根目录下放一个init的可执行文件,也就是说Linux系统的init进程在内核初始化完成后,就直接执行init这个文件。

2.2Init进程启动后,做了哪些事?

Android 10.0系统启动之init进程(一)-「Android取经之路」

Init进程启动后,首先挂载文件系统、再挂载相应的分区,启动SELinux安全策略,启动属性服务,解析rc文件,并启动相应属性服务进程,初始化epoll,依次设置signal、property、keychord这3个fd可读时相对应的回调函数。进入无线循环,用来响应各个进程的变化与重建。


3.kernel启动init进程 源码分析

kernel/msm-4.19/init/main.c

kernel_init()
|
run_init_process(ramdisk_execute_command) //运行可执行文件,启动init进程
static int __ref kernel_init(void *unused)
{
\tkernel_init_freeable(); //进行init进程的一些初始化操作
\t/* need to finish all async __init code before freeing the memory */
\tasync_synchronize_full();// 等待所有异步调用执行完成,,在释放内存前,必须完成所有的异步 __init 代码
\tfree_initmem();// 释放所有init.* 段中的内存
\tmark_rodata_ro(); //arm64空实现
\tsystem_state = SYSTEM_RUNNING;// 设置系统状态为运行状态
\tnuma_default_policy(); // 设定NUMA系统的默认内存访问策略
\tflush_delayed_fput(); // 释放所有延时的struct file结构体
\tif (ramdisk_execute_command) { //ramdisk_execute_command的值为"/init"
\t\tif (!run_init_process(ramdisk_execute_command)) //运行根目录下的init程序
\t\t\treturn 0;
\t\tpr_err("Failed to execute %s\\n", ramdisk_execute_command);

\t}
\t/*
\t * We try each of these until one succeeds.
\t *
\t * The Bourne shell can be used instead of init if we are
\t * trying to recover a really broken machine.
\t */
\tif (execute_command) { //execute_command的值如果有定义就去根目录下找对应的应用程序,然后启动
\t\tif (!run_init_process(execute_command))
\t\t\treturn 0;
\t\tpr_err("Failed to execute %s. Attempting defaults...\\n",
\t\t\texecute_command);
\t}
\tif (!run_init_process("/sbin/init") || //如果ramdisk_execute_command和execute_command定义的应用程序都没有找到,
\t//就到根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动
\t !run_init_process("/etc/init") ||
\t !run_init_process("/bin/init") ||
\t !run_init_process("/bin/sh"))
\t\treturn 0;
\tpanic("No init found. Try passing init= option to kernel. "
\t "See Linux Documentation/init.txt for guidance.");
}
kernel_init_freeable()
|
do_basic_setup()
static void __init do_basic_setup(void)
{
\tcpuset_init_smp();//针对SMP系统,初始化内核control group的cpuset子系统。
\tusermodehelper_init();// 创建khelper单线程工作队列,用于协助新建和运行用户空间程序
\tshmem_init();// 初始化共享内存
\tdriver_init();// 初始化设备驱动
\tinit_irq_proc();//创建/proc/irq目录, 并初始化系统中所有中断对应的子目录
\tdo_ctors();// 执行内核的构造函数
\tusermodehelper_enable();// 启用usermodehelper
\tdo_initcalls();//遍历initcall_levels数组,调用里面的initcall函数,这里主要是对设备、驱动、文件系统进行初始化,

\t//之所有将函数封装到数组进行遍历,主要是为了好扩展
\trandom_int_secret_init();//初始化随机数生成池
}

小结:

本节主要介绍了Init进程的一些概况,包括启动后的具体功能,下一节主要针对Init进程的源码进行分析。

Android 10.0系统启动之init进程(一)-「Android取经之路」


分享到:


相關文章: