init 进程是 Android 系统中用户空间的第一个进程,进程号为 1,是 Android 系统启动流程中一个关键的步骤,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建 Zygote (孵化器) 和属性服务等。init 进程是由多个源文件共同组成的,这些文件位于源码目录 system/core/init 中。
引入 init 进程
为了讲解 init 进程,首先了解一下 Android 系统启动流程的前几步,以引入 init 进程。
1)启动电源以及系统启动
当电源按下时引导芯片代码从预定义的地方 (固化在 ROM) 开始执行。加载引导程序 BootLoader 到 RAM 中,然后执行。
2)引导程序 BootLoader
引导程序 BootLoader 是在 Android 系统启动前的一个小程序,它的作用主要是把系统 OS 拉起来并运行。
3)Linux 内核启动
当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,它首先在系统文件中寻找 init.rc 文件,并启动 init 进程。
4)init 进程启动
init 进程做的工作比较多,主要用来初始化和属性服务,也用来启动 Zygote 进程。
从上面的步骤可以看出,当我们按下电源时,系统启动会加载引导程序,引导程序又启动 Linux 内核,在 Linux 内核完成后,第一件事就是要启动 init 进程。
init 进程的入口函数
在 Linux 内核加载完成后,它首先在系统文件中寻找 init.rc 文件,并启动 init 进程,init 进程的入口函数 main,代码如下:
int main(int argc, char** argv) { |
1) 注释 1 和 3 都是关于属性服务
在注释 1 处 property_init
函数来对属性进行厨师阿虎,并在注释 3 处调用 start_property_service
启动属性服务
2)注释 2 处设置子进程信号处理函数
注释 2 处调用 sigchld_handler_init
用于设置子进程信号处理函数,主要用于防止 init 进程的子进程成为僵尸进程,为了防止僵尸进程的出现系统会在子进程暂停和终止的时候发出 SIGCHLD 信号,而 sigchld_handler_init
函数就是用来接收这个信号的(其内部只处理进程终止的 SIGCHLD 信号)。
在 UNIX/Linux 中,父进程使用 fork 创建子进程,在子进程终止之后,如果父进程并不知道子进程已经终止了,这时子进程虽然已经推出了,但是在系统进程表中还为它保留了一定的信息(比如进程号、退出状态、运行时间等),这个子进程就被称作僵尸进程。系统进程表示一项有限资源,如果被僵尸进程耗尽的话,系统就可能无法创建新的进程了。
3)注释 5 负责重启 init 子进程
Zygote 也是 init 进程的子进程之一,如果出现子进程终止的情况就会触发 sigchld_handler_init
并触发清理 Zygote 进程的信息,然后 Zygote 就会在注释 5 处被重启。
4)注释 4 用来解析 init.rc 文件
init.rc
是一个非常重要的配置文件,它是由 Android 初始化语言(Android Init Language)编写的脚本,这种语言主要包含 5 种类型语句:Action、Command、Service、Option 和 Import。
Zygote 的配置与启动
1)Zygote 配置
从 Android 8.0 开始对 init.rc
文件进行了拆分,每个服务对应一个 rc 文件,下面是 Zygote 启动脚本,在 init.zygote64.rc
中。代码如下:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server |
Service 命令用于通知 init 进程 Zygote 进程的名为 zygote,这个进程执行程序的路径为 system/bin/app_process64
,后面的代码是要传给 app_process64
的参数。class main
指的是 Zygote 的 classname 为 main。
Service 命令会由 init main 函数的代码中解析为 Service 对象,再添加到 ServiceManager 中 vector 类型的 Service 链表中,执行 Service 命令解析的主要涉及文件是是 system/core/init/service.cpp
执行的时候
2)Zygote 进程的真正启动
init.rc 是一个配置文件,在 Service 命令定义 Zygote 后还有如下配置代码:
on nonencrypted |
其中 clas_start 对应的函数执行是 system/core/init/builtins.cpp
中的 do_class_start
,这里就是启动 classname 为 main 的 Service,其中 zygote 就是在此处启动的。如下:
static Result<Success> do_class_start(const BuiltinArguments& args) { |
其中 Zygote 执行程序的路径是 /system/bin/app_process64
对应的文件为 framework/base/cmds/app_process/app_main.cpp
,部分代码如下:
int main(int argc, char* const argv[]) |
- 如上注释 1 处调用 runtime 的 start 函数启动 Zygote,至此 Zygote 就启动了。
Android 系统的属性服务
Android 系统提供了属性服务,其实是类似于 Windows 系统中的注册表管理器,用于记录用户的一些使用信息,从而在系统或软件重启后,扔能够获取存储的记录,进行相应的初始化工作。
init 进程启动时会启动属性服务,并为其分配内存,用来存储这些属性,如果需要属性直接读取就可以了,上面 init.cpp
的提到的 property_init
, start_property_service
就是用于初始化属性配置和启动属性服务的。
void start_property_service() { |
- 属性服务通过创建非阻塞的 Socket
- 属性服务通过 listen 函数对 property_set_fd 进行设置最多同时可以为 8 个试图设置属性服务的用户提供服务。
- 最终将 property_set_fd 放入 epoll 中,用 epoll 监听收到新数据的时候回调
handle_property_set_fd
函数
ps: epoll 为 Linux 内核为处理大批量文件描述符而做了改进的 poll,是 Linux 下多路复用 I/O 接口 select/poll 的增强版本,epoll 内部使用数据结构是红黑树,而 select 使用数组,当存在大量文件描述符时,epoll 查找效率会比 select 速度快。
属性服务中分为控制属性和普通属性,控制属性用来执行一些命令,比如开机的动画就使用了这个属性。控制属性和普通属性的存储、更新逻辑不一致,控制属性的 key 名称以 ctl.
开头。
总结
init 进程启动做了很多工作,总的来说做了三件事
- 创建和挂载启动所需的文件目录
- 初始化和启动属性服务
- 解析 init.rc 并执行启动 Zygote 进程