Android Framework源码之init进程解析
[TOC]
文章参考:http://kmanong.top/kmn/qxw/form/article?id=14312&cate=45
文章参考:https://juejin.cn/post/6844903965688250382
源码分析基于Android12
概述
我们先来看一下Android系统启动流程的全过程。
- 按电源键
- 进入开机动画
- 经过漫长的等待
- 开机动画结束
- 正式开机,进入设置画面
- 进入系统桌面(Launcher)
1.BootLoader
刷过机的朋友大概都知道,Android可以通过某个组合按键进入BootLoader页面,这个也就是上图中的最底层,在Android系统,甚至于它的内核还未加载时的一个引导程序,主要负责对kenel进行解压和初始化的工作
2.idle进程
kernel中的idle进程是0号进程,由内核中启动,并始终执行在内核态,由内核态的idle进程开启我们常提的1号进程,init(对应源码,system/core/init/init.cpp
)
3.init进程
init.cpp
中做的事情其实不只是开启init
进程,这个后面分析具体源码时再做详细介绍
init进程负责的事情主要是对init.rc
这个系统启动脚本文件进行解析(loadBootScripts()
),经过对ro.zygote
对应的具体的架构的脚本文件进行解析,进行到第四步
4.zygote进程
zygote的进程的启动入口。我们找到对应的源码位置在framework/base/cmds/app_process/app_main
在app_process
中把app_main
运行的进程设置process_name
为zygote,这也就是我们在执行ps
指令时看到的zygote
进程了,此时zygote还处于native
层,通过jni调用zygoteInit.java
中的main
函数,正式进入Java
世界,zygoteInit中又开启了SystemServer
,进入第四步
5.SystemServer进程
这个是Android中最核心的进程。SystemServer进程主要用于创建系统服务,我们熟悉的AMS、WMS、PMS都是由SystemServer创建的。这个我们后面会一一学习到。
6.Launcher
Launcher已经是我们具体的App应用了,这个是由zygote进程fork出来的进程
init进程启动
init
进程是第一个进程,或者我们可以说它是所有进程的父进程或者父父进程。
init
进程有两个职责:
挂载/sys,/dev或/proc之类的目录
运行/init.rc脚本。init.rc负责系统的初始化设置
init的源码位于system/core/init
包下,我们先从入口类main.cpp
来看
1 | /** |
main.cpp的函数跟之前版本有了很大的区别。
SetupSelinux
1 | int SetupSelinux(char** argv) { |
我们可以看到在SetupSelinux的函数里面调用execv开启init进程‘
FirstStageMain函数
我们再来看下第一阶段的函数FirstStageMain做了哪些工作
1 | /** |
主要代码处已经做处了注释,现在来总结下FirstStageMain的工作:
- 处理init进程挂掉的情况
- 设置用户组,挂载相关系统文件
- 根据
/force_debuggable
文件来判断是否允许adb root指令 - 找到init的二进制文件目录,通过execv来启动init进程
至此init进程就被启动起来了,我们在回来看看SecondStageMain函数接下来做了哪些工作
SecondStageMain函数
1 | /** |
SecondStageMain的主要工作总结
在Android12的版本中,该类的入口函数为main,代码中的关键部分我已经做了注释,现在来我们来总结一下SecondStageMain的主要工作:
使用epoll对init子进程的信号进行监听
初始化系统属性,使用mmap共享内存,”/dev/properties/property_info” (重要)
开启属性服务,并注册到epoll中(重要)
加载系统启动脚本”/init.rc”
解析启动脚本,启动相关服务
属性服务
什么是属性服务,我觉得它更像关于这台手机的各种系统信息,通过 key / value 的形式供我们所有程序使用,下面内容就是我的模拟器进入 adb shell 后获取到的属性值,下面我从输出结果里面保留的一部分:
1 | generic_x86:/ $ getprop |
属性服务相关代码在 SecondStageMain 阶段其实主要做了三件事:创建共享内存、加载各种属性值以及创建属性服务的 Socket。下面是这关于这几部分的片段:
PropertyInit初始化
1 | /** |
分析到这里,系统属性的初始化终于分析完了,现在来总结下,系统属性初始化是使用了mmap的内存共享机制,来让其他进程来获取系统属性的。
那既然可以通过共享内存来访问,为什么还需要开启一个属性服务呢?直接通过共享内存来设置系统属性,不就好了么?
这里其实就涉及到安全性的问题了,如果所有进程都可以自由的修改系统属性,那系统属性,还能被成为系统属性吗?所以Android设计为,其他进程只能通过共享内存来获取系统属性,而“修改”的权限则统一收拢到init进程中,开启一个属性服务。
1 | void StartPropertyService(int* epoll_socket) { |
开启属性服务的重要步骤已经注释说明了,现在再来总结下:
系统服务开启流程
- 创建socket服务端
- 监听sokcet服务,最大并发数是8
- 注册到epoll的handler中进行IO优化处理
至此,init进程的主要工作流程和重要原理已分析完成
init.rc 解析
init.rc 是什么?它是非常重要的配置文件,而且众多 rc 文件中 init.rc 是最主要的文件,不过这里我不会讲 rc 文件的语法是怎么样的,因为 system/core/init/README.md 中已经写的很清楚了,init.rc 会根据 on 分成不同阶段,并且由 trigger 进行不同阶段的触发,而每个阶段里面就是一条条要执行指令,比如 start 后面跟的就是要启动的服务,mkdir 就是创建目录。
既然分成了多个阶段,那先来看看触发阶段是怎么样的: