感谢您的阅读与点赞!欢迎点击右上角关注:「大猫玩程序」VX公众号:大猫玩程序
上一节主要讲解了Init进程的第二阶段启动过程以及信号处理过程。
Android 10.0系统源码取经之路——启动篇
Android系统架构浅析-「Android取经之路」
Android是怎么启动的-「Android取经之路」
Android 10.0系统启动之init进程(一)-「Android取经之路」
Android 10.0系统启动之init进程(二)-「Android取经之路」
Android 10.0系统启动之init进程(三)-「Android取经之路」
本节主要讲解init进程第二阶段的属性服务和init.rc启动流程
6.属性服务
我们在开发和调试过程中看到通过property_set可以轻松设置系统属性,那干嘛这里还要启动一个属性服务呢?这里其实涉及到一些权限的问题,不是所有进程都可以随意修改任何的系统属性,
Android将属性的设置统一交由init进程管理,其他进程不能直接修改属性,而只能通知init进程来修改,而在这过程中,init进程可以进行权限控制,我们来看看具体的流程是什么。
6.1 property_init
代码路径:platform/system/core/property_service.cpp
作用:初始化属性系统,并从指定文件读取属性,并进行SELinux注册,进行属性权限控制
清除缓存,这里主要是清除几个链表以及在内存中的映射,新建property_filename目录,这个目录的值为 /dev/_properties_
然后就是调用CreateSerializedPropertyInfo加载一些系统属性的类别信息,最后将加载的链表写入文件并映射到内存
通过CreateSerializedPropertyInfo 来加载以下目录的contexts:
1)与SELinux相关
/system/etc/selinux/plat_property_contexts/vendor/etc/selinux/vendor_property_contexts/vendor/etc/selinux/nonplat_property_contexts/product/etc/selinux/product_property_contexts/odm/etc/selinux/odm_property_contexts
2)与SELinux无关
/plat_property_contexts/vendor_property_contexts/nonplat_property_contexts/product_property_contexts/odm_property_contexts
6.2 StartPropertyService
代码路径:platform/system/core/init.cpp
作用:启动属性服务
首先创建一个socket并返回文件描述符,然后设置最大并发数为8,其他进程可以通过这个socket通知init进程修改系统属性,
最后注册epoll事件,也就是当监听到property_set_fd改变时调用handle_property_set_fd
6.3 handle_property_set_fd
代码路径:platform/system/core/property_service.cpp
作用:建立socket连接,然后从socket中读取操作信息,根据不同的操作类型,调用HandlePropertySet做具体的操作
HandlePropertySet是最终的处理函数,以"ctl"开头的key就做一些Service的Start,Stop,Restart操作,其他的就是调用property_set进行属性设置,不管是前者还是后者,都要进行SELinux安全性检查,只有该进程有操作权限才能执行相应操作。
7.第三阶段init.rc
当属性服务建立完成后,init的自身功能基本就告一段落,接下来需要来启动其他的进程。但是init进程如何其他其他进程呢?其他进程都是一个二进制文件,我们可以直接通过exec的命令方式来启动,例如 ./system/bin/init second_stage,来启动init进程的第二阶段。但是Android系统有那么多的Native进程,如果都通过传exec在代码中一个个的来执行进程,那无疑是一个灾难性的设计。
在这个基础上Android推出了一个init.rc的机制,即类似通过读取配置文件的方式,来启动不同的进程。接下来我们就来看看init.rc是如何工作的。
init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本。
init.rc在手机的目录:./init.rc
init.rc主要包含五种类型语句:
- Action
- Command
- Service
- Option
- Import
7.1 Action
动作表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个动作
Action: 通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:
- on early-init; 在初始化早期阶段触发;
- on init; 在初始化阶段触发;
- on late-init; 在初始化晚期阶段触发;
- on boot/charger: 当系统启动/充电时触发;
- on property:=: 当属性值满足条件时触发;
7.2 Command
command是action的命令列表中的命令,或者是service中的选项 onrestart 的参数命令,命令将在所属事件发生时被一个个地执行.
下面列举常用的命令
- class_start : 启动属于同一个class的所有服务;
- class_stop : 停止指定类的服务
- start : 启动指定的服务,若已启动则跳过;
- stop : 停止正在运行的服务
- setprop :设置属性值
- mkdir :创建指定目录
- symlink : 创建连接到的符号链接;
- write : 向文件path中写入字符串;
- exec: fork并执行,会阻塞init进程直到程序完毕;
- exprot :设定环境变量;
- loglevel :设置log级别
- hostname :设置主机名
- import :导入一个额外的init配置文件
7.3 Service
服务Service,以 service开头,由init进程启动,一般运行在init的一个子进程,所以启动service前需要判断对应的可执行文件是否存在。
命令:
service [ ]*
init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。
例如: service servicemanager /system/bin/servicemanager代表的是服务名为servicemanager,服务执行的路径为/system/bin/servicemanager。
7.4 Options
Options是Service的可选项,与service配合使用
- disabled: 不随class自动启动,只有根据service名才启动;
- oneshot: service退出后不再重启;
- user/group: 设置执行服务的用户/用户组,默认都是root;
- class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;
- onrestart:当服务重启时执行相应命令;
- socket: 创建名为/dev/socket/的socket
- critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式
default: 意味着disabled=false,oneshot=false,critical=false。
7.5 import
用来导入其他的rc文件
命令:
import
7.6 init.rc 解析过程
7.6.1 LoadBootScripts
代码路径:platformsystemcoreinitinit.cpp
作用:如果没有特殊配置ro.boot.init_rc,则解析./init.rc
把/system/etc/init,/product/etc/init,/product_services/etc/init,/odm/etc/init,
/vendor/etc/init 这几个路径加入init.rc之后解析的路径,在init.rc解析完成后,解析这些目录里的rc文件。
Android7.0后,init.rc进行了拆分,每个服务都有自己的rc文件,他们基本上都被加载到/system/etc/init,/vendor/etc/init, /odm/etc/init等目录,等init.rc解析完成后,会来解析这些目录中的rc文件,用来执行相关的动作。
代码路径:platformsystemcoreinitinit.cpp
作用:创建Parser解析对象,例如service、on、import对象
7.6.2 执行Action动作
按顺序把相关Action加入触发器队列,按顺序为 early-init -> init -> late-init. 然后在循环中,执行所有触发器队列中Action带Command的执行函数。
7.6.2 Zygote启动
从Android 5.0的版本开始,Android支持64位的编译,因此zygote本身也支持32位和64位。通过属性ro.zygote来控制不同版本的zygote进程启动。
在init.rc的import段我们看到如下代码:
import /init.${ro.zygote}.rc // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件
init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。
分别是
init.zygote32.rc,
init.zygote32_64.rc,
init.zygote64.rc,
init.zygote64_32.rc,由硬件决定调用哪个文件。
这里拿64位处理器为例,init.zygote64.rc的代码如下所示:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 解析:service zygote :init.zygote64.rc 中定义了一个zygote服务。init进程就是通过这个service名称来创建zygote进程/system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server解析:zygote这个服务,通过执行进行/system/bin/app_process64 并传入4个参数进行运行:参数1:-Xzygote 该参数将作为虚拟机启动时所需的参数参数2:/system/bin代表虚拟机程序所在目录参数3:--zygote指明以ZygoteInit.java类中的main函数作为虚拟机执行入口参数4:--start-system-server告诉Zygote进程启动systemServer进程
8.总结
init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略。
init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些功能落实。
init进行第三阶段主要是解析init.rc 来启动其他进程,进入无限循环,进行子进程实时监控。