Init启动流程
Init介绍
Init进程是Android系统中用户空间的第一个进程(pid=1),它是用户进程的鼻祖,负责孵化各种属性服务、守护进程也包括Zygote。Init是由多个源文件共同组成的,这些文件位于/system/core/init。
启动过程
Kernel启动找到Init进程后,进程入口为源码init目录下的main.cpp的main()。
1 |
|
FirstStageMain
1 |
|
主要通过mount挂载对应的文件系统,mkdir创建对应的文件目录,并配置相应的访问权限。
这些文件只是在应用运行的时候存在,一旦应用运行结束就会随着应用一起消失。
挂载的文件系统主要有四类:
tmpfs
:一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中。由于tmpfs是驻留在RAM的,因此它的内容是不持久的。断电后,tmpfs的内容就消失了。devpts
:为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态地创建一个新的pty设备文件。proc
:也是一个虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。sysfs
:与proc文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys目录下。作用是把系统的设备和总线按层次组织起来,使得它们可以在用户空间读取,用来向用户空间导出内核的数据结构和属性。
在FirstStageMain还会通过InitKernelLogging(argv)来初始化log日志系统。此时Android还没有自己的系统日志,采用kernel的log系统,打开的设备节点/dev/kmsg
, 可通过cat /dev/kmsg
来获取内核log。
最后会通过execv方法传递对应的path与下一阶段的参数selinux_setup。
SetupSelinux
1 |
|
SetupSelinux方法中加载了selinux的策略并启动selinux的强制模式,然后启动了Init进程。Init的二进制文件存放在机器的/system/bin/init,然后通过execv启动Init进程第二阶段。
SecondStageMain
1 |
|
LoadBootScripts()会加载init.rc配置文件,之后加载/{system, vendor, odm}/etc/init/下(Android设备中的目录)的所有rc配置文件。
LoadBootScripts
1 |
|
init.rc
init.rc有两个,分别位于:
/system/core/rootdir/init.rc,正常启动
/bootable/recovery/etc/init.rc,刷机
import
1 |
|
init.rc中会根据系统的不同属性来引入不同的zygote脚本。
- init.zygote32.rc:zygote进程对应的执行程序是app_process(纯32bit模式)
- init.zygote64.rc:zygote进程对应的执行程序是app_process(纯64bit模式)
- init.zygote64_32.rc:启动两个zygote进程(zygote和zygote_secondary),对应的执行程序分别是app_process64(主模式),app_process32
on early-init
1 |
|
early-init中启动了ueventd服务和apex相关服务。
ueventd服务
1
2
3
4
5service ueventd //ueventd服务的可执行文件的路径为/system/bin/ueventd
class core //ueventd归属于core class,同样归属于core class的还有adbd、console等服务
critical //表明这个Service对设备至关重要,如果Service在四分钟内退出超过4次,则设备将重启进入恢复模式。
seclabel u:r:ueventd:s0 //selinux相关的配置
shutdown critical //ueventd服务关闭行为early-init触发时机
1
2
3/system/core/init.init.cpp$SecondStageMain
am.QueueEventTrigger("early-init");
on init
1 |
|
on late-init
1 |
|
zygote
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal该文件通过service语句来创建zygote进程,该进程的代码位于/system/bin/app_process目录下。当相关的触发器被触发后,便会启动zygote进程。
总结
Init入口中包含5个分支:
- ueventd:实际上就是Init程序的软链接,在init.rc的early-init阶段启动
- selinux_setup:FitstStageMain中启动
- subcontext:SecondStageMain中启动
- second_stage:在selinux_setup启动完之后执行
- first_stage:默认首先执行
Init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略。
Init进程第二阶段的主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务。
Init进程第三阶段主要是解析init.rc来启动其他进程,进入死循环,进行子进程实时监控。
在SecondStage,首先加载init.rc配置,然后再依次在Android设备中查找以下三种配置,并加载:
- /system/etc/init/
- /vendor/etc/init/
- /odm/etc/init/
Android根文件系统的镜像中不存在/dev目录,该目录是Init进程启动后动态创建的。为此,Init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。
ueventd通过两种方式创建设备节点文件:
冷插拔(Cold Plug)
以预先定义的设备信息为基础,当ueventd启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。
热插拔(Hot Plug)
在系统运行中,当有设备插入USB端口时,ueventd就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。
signal:每个进程在处理其他进程发送的signal信号时都需要先注册,当进程的运行状态改变或终止时会产生某种signal信号,Init进程是所有用户空间进程的父进程,当其子进程终止时产生signal信号,以便父进程进行处理,主要是为了防止子进程成为僵尸进程。
僵尸进程:父进程使用fork创建子进程,子进程终止后,如果父进程不知道子进程已经终止的话,这时子进程虽然已经退出,但是在系统进程表中还为它保留了一些信息(如进程号、运行时间、退出状态等),这个子进程就是僵尸进程。系统进程表是一项有限的资源,如果它被僵尸进程耗尽的话,系统可能会无法创建新的进程。