上电加载—>init进程启动可以参考 Android 最新最全面启动流程分析(含 TEE、安全验证等)
一、init程序源码和构建
在源码中init程序代码位于/system/core/init,其编译文件Android.bp: init.rc等文件都在/system/core/rootdir中.
//这里init_second_stage容易迷惑人,实际上它包含了first_stage逻辑
cc_defaults {
name: "init_second_stage_defaults",
stem: "init",
defaults: ["init_defaults"],
srcs: ["main.cpp"],
symlinks: ["ueventd"],
}
cc_binary {
name: "init_second_stage",
defaults: ["init_second_stage_defaults"],
}
构建系统会将rootdir下的rc文件和init程序打包进init_boot.img镜像中,内核启动后就会加载init_boot执行其中的init程序。
二、init程序执行流程
1、init程序的入口main.cpp(/system/core/init/main.cpp)
//init进程分阶段,刚开始,可以看到有五条分支,第一次进入内核不带参数,所以进入FirstStageMain
int main(int argc, char** argv) {
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
//.....
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
2、FirstStageMain 第一阶段初始化(/system/core/init/first_stage_init.cpp)
int FirstStageMain(int argc, char** argv) {
//设置环境变量
setenv("PATH", _PATH_DEFPATH, 1)
//构建初始文件系统tmpfs(内存中),创建基础文件目录
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")
...
//获取cmdline、bootconfig
android::base::ReadFileToString("/proc/cmdline", &cmdline);
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
...
//从cmdline、bootconfig中获取BOOT模式(充电、recovery,正常)
BootMode boot_mode = GetBootMode(cmdline, bootconfig);
//根据模式去加载不同内核模块
LoadKernelModules(boot_mode, want_console,
want_parallel, module_count)
//执行第一阶段文件系统挂载,最主要是/system挂载
fsm->DoFirstStageMount();
...
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
...
execv(path, const_cast<char**>(args));
}
在/system挂载后,执行了/system/bin/init,其实这个init和init_boot中是同一个,只是放在了两个镜像中,本质都是同一份代码构建的,所以启动/system/bin/init,就相当于启动/init自身,execv 是一个系统调用,传入参数selinux_setup,当前进程的代码、数据、堆栈被替换,进程ID不变,可以理解为init自己调用自己。
3、执行selinux_setup (/system/core/init/main.cpp)
int main(int argc, char** argv){
....
return SetupSelinux(argv);
...
}
SetupSelinux执行(/system/core/init/selinux.cpp)
int SetupSelinux(char** argv) {
...
//加载 selinux 策略
//1、内部先挂在其他没有挂载好的分区MountMissingSystemPartitions();
//2、读取所有分区的策略文件 ReadPolicy(&policy);
//3、加载策略LoadSelinuxPolicy(policy);
//配置selinux挂载路径
// set_selinuxmnt("/sys/fs/selinux");
//正式加载,会调用内核
// security_load_policy(policy.data(), policy.size()
LoadSelinuxPolicyAndroid();
//配置selinux为enforce模式,强制模式
SelinuxSetEnforcement();
//重置init执行文件的selinux上下文
selinux_android_restorecon("/system/bin/init", 0);
//进入second_stage
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
}
4、执行second_stage (/system/core/init/main.cpp)
int main(int argc, char** argv){
....
return SecondStageMain(argv);
...
}
SecondStageMain执行(/system/core/init/init.cpp)
int SecondStageMain(int argc, char** argv) {
//一、selinux属性上下文初始化:PropertyInit();
//1、配置selinux权限检查回调
selinux_set_callback(SELINUX_CB_AUDIT, cb);
//2、创建属性设备节点 mkdir("/dev/__properties__")
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
//3、加载各分区中的selinux属性上下文
LoadPropertyInfoFromFile("分区/etc/selinux/plat_property_contexts"
//4、将属性上下文写入/dev/__properties__/property_info),它自身的文件上下文为u:object_r:default_prop:s0
WriteStringToFile(serialized_contexts, "/dev/__properties__/property_info", 0444, 0, 0, false)
----------------------------
//二、系统属性系统
//初始化系统属性区域,实际调用代码/bioinc/libc/system_properties/system_properties.cpp--->SystemProperties::AreaInit
//内部会读取上面写入的property_info
//然后mmap申请一块匿名内存,在这块内存中创建上下文节点列表
//创建/dev/__properties__/properties_serial
//将properties_serial通过mmap映射到内存
__system_property_area_init();
//提取设备树、cmdline、bootconfig中的属性信息
ProcessKernelDt();
ProcessKernelCmdline();
ProcessBootconfig();
//配置kernelbootprop属性信息,就是给上面没获取到的关键属性赋默认值,获取到的就忽略
ExportKernelBootProps();
//读取从各分区的xxx.prop,加入属性列表
PropertyLoadBootDefaults();
//读取自定义属性,加入属性列表
PropertyLoadDerivedDefaults();
//以上添加属性都是通过__system_property_find先查找
//如果存在就__system_property_update, 不存在就__system_property_add
//实际调用/bioinc/libc/system_properties/system_properties.cpp--->SystemProperties::Update|Add
//实际添加到mmap内存区域,也就是/dev/__properties__/properties_serial中
----------------
//挂载扩展文件系统,主要是将tmpfs挂载到/apex,为后续挂载做准备
MountExtraFilesystems();
//selinux标签初始化,就是将所有关键目录加上selinux标签,并且恢复到policy定义的状态
SelabelInitialize();
SelinuxRestoreContext();
//注册信号监听处理,处理子进程的退出,还有fork后的回调
InstallSignalFdHandler(&epoll);
//处理线程通知,通过eventfd(0)来处理其他线程发来的通知
InstallInitNotifier(&epoll);
//启动属性服务
//调用property_service.cpp的StartPropertyService
//1.初始化版本InitPropertySet("ro.property_service.version", "2");
//2.开启两个线程打开socket,放入epoll监听(一个给系统用property_service_for_system,一个给普通程序用property_service)
//3.有写入属性的请求会回调handle_property_set_fd,还是通过上面的方式写入到/dev/__properties__/properties_serial。
//4.在epoll中还监听了init自身的socket,处理函数HandleInitSocket,用来处理PersistentProperties这种重新也生效的属性
StartPropertyService(&property_fd);
----------
//初始化init子环境,该方法会fork一个进程执行一些特权操作,这样不破坏主init进程环境
//对应入口main.cpp---> return SubcontextMain(argc, argv, &function_map); ,子进程通过socketpair和init主进程通信
InitializeSubcontext();
------------
//创建动作管理和服务列表
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
//加载启动脚本
//创建解析器Parser parser = CreateParser(action_manager, service_list);
//解析各个分区下 /分区/etc/init 目录中的rc文件
//am将各种action按先后顺序入队列
LoadBootScripts(am, sm);
while(true){
....
//处理关机命令
HandlePowerctlMessage(*shutdown_command);
//执行rc脚本
am.ExecuteOneCommand()
....
//epoll定时唤醒,或者有上面监听的fd写入也会唤醒
auto epoll_result = epoll.Wait(epoll_timeout);
if (!IsShuttingDown()) {
//处理外部控制命令,如开启start、停止stop、重启resart 那些rc中注册的服务
HandleControlMessages();
//设置usb控制
SetUsbController();
}
}
}
5、init..rc执行 (/system/core/rootdir/init..rc)
后面所有进程的启动就需要查看init.*.rc了。
-
import示例
#init.rc
import /init.environ.rc
#init.environ.rc
on early-init
export ANDROID_BOOTLOGO 1
.....
-
action示例
其中on是固定写法,early-init为触发条件,export …为命令行
#init.environ.rc
on early-init
export ANDROID_BOOTLOGO 1
-
service示例
其中service固定写法, zygote名称 ,/…为程序路径,后面的是参数 第二行起就是程序的配置项
#init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
-
下面枚举了绝大部分触发条件和命令,以及service相关的配置
aticon触发类型 | 实际条目 |
---|---|
boot阶段触发器 | on early-init - 在早期初始化阶段 |
on init - 在初始化阶段 |
|
on late-init - 在后期初始化阶段 |
|
on post-fs - 文件系统挂载后 |
|
on post-fs-data - 数据分区挂载后 |
|
on boot - 系统启动完成时 |
|
on charger - 充电模式启动时 |
|
属性变化触发器 | on property:<key>=<value> - 当指定属性值匹配时触发 |
on property:<key>=* - 当属性值发生变化时触发(不检查具体值) |
|
设备相关触发器 | on device-added-<path> - 当设备节点添加时 |
on device-removed-<path> - 当设备节点移除时 |
|
文件系统触发器 | on fs - 文件系统挂载时 |
on zygote-start - Zygote进程启动时 |
|
其他触发器 | on early-boot - 早期启动阶段 |
on nonencrypted - 当设备未加密时 |
|
on load_persist_props - 加载持久属性时 |
|
on load_all_props - 加载所有属性时 |
|
on firmware_mounts_complete - 固件挂载完成时 |
可用命令类型 | 实际命令 |
---|---|
进程管理 | start <service> - 启动指定服务 |
stop <service> - 停止指定服务 |
|
restart <service> - 重启指定服务 |
|
exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ] - 执行命令并替换当前进程 |
|
文件系统操作 | mkdir <path> [mode] [owner] [group] - 创建目录 |
symlink <target> <path> - 创建符号链接 |
|
write <path> <content> - 向文件写入内容 |
|
copy <src> <dst> - 复制文件 |
|
chown <owner> <group> <path> - 更改文件所有者 |
|
chmod <mode> <path> - 更改文件权限 |
|
restorecon <path> - 恢复SELinux上下文 |
|
restorecon_recursive <path> - 递归恢复SELinux上下文 |
|
属性操作 | setprop <name> <value> - 设置系统属性 |
trigger <event> - 触发另一个事件 |
|
设备操作 | insmod <path> - 加载内核模块 |
rmmod <name> - 移除内核模块 |
|
setrlimit <resource> <cur> <max> - 设置资源限制 |
|
系统控制 | class_start <classname> - 启动指定类别的所有服务 |
class_stop <classname> - 停止指定类别的所有服务 |
|
class_reset <classname> - 重置指定类别的所有服务 |
|
domainname <name> - 设置域名 |
|
hostname <name> - 设置主机名 |
|
mount <type> <device> <dir> [ <flag>\* ] [<options>] - 挂载文件系统 |
|
umount <path> - 卸载文件系统 |
|
swapon_all <fstab> - 启用所有交换分区 |
|
wait <path> [ <timeout> ] - 等待文件存在 |
|
wait_for_prop <name> <value> - 等待属性值匹配 |
|
日志和调试 | loglevel <level> - 设置内核日志级别 |
load_all_props - 加载所有属性文件 |
|
load_persist_props - 加载持久属性 |
|
安全相关**** | setcon <context> - 设置当前SELinux上下文 |
setenforce <0\|1> - 设置SELinux强制模式 |
|
setkey <keycode> <value> - 设置键盘映射 |
|
****电源管理 | powerctl - 电源控制命令 |
service配置类型 | 实际options |
---|---|
执行控制选项 | |
disabled - 服务不随class自动启动,必须显式启动 |
|
oneshot - 服务退出后不自动重启 |
|
onrestart - 当服务重启时执行一个命令 |
|
critical - 关键服务,频繁崩溃会触发系统恢复 |
|
restart_periodic <seconds> - 周期性重启策略 |
|
shutdown <shutdown_behavior> - 设置关机行为 |
|
onrestart - 服务重启时执行的命令 |
|
用户与权限类 | |
用户和组配置 | user <username> - 运行身份(如 system , root ) |
group <group> [附加组...] - 主组和附加组 |
|
supplementarygids <gid...> - 补充组ID(如 supplementarygids 1000 1001 ) |
|
capabilities <cap...> - Linux Capabilities(如 capabilities NET_ADMIN SYS_ADMIN ) |
|
seclabel <SELinux上下文> - 强制SELinux标签(如 seclabel u:r:hal_bluetooth:s0 ) |
|
namespace <pid|mnt|net> - 进入命名空间(如 namespace net ) |
|
资源限制 | |
rlimit <resource> <cur> <max> - 设置资源限制 |
|
常见资源类型: 1. cpu - CPU时间(秒) 2. nofile - 文件描述符数量 3. memlock - 锁定内存大小 4. nproc - 最大进程数 |
|
安全配置 | |
priority <priority> - 设置调度优先级(-20到19) |
|
ioprio <class> <0-7> - I/O调度优先级 |
|
class类型: 0:none 1:realtime 2:best-effort(默认) 3:idle | |
文件与通信类 | |
socket <名称> <类型> <权限> [user] [group] [seclabel] |
|
类型:stream /dgram /seqpacket |
|
示例:socket mysocket dgram 660 root system |
|
file <路径> <r|w|rw> - 预打开文件(如 file /data/log.txt rw ) |
|
writepid <文件...> - 写入PID到文件(如 writepid /dev/cpuset/foreground/tasks ) |
|
环境配置 | |
env <key> <value> - 设置环境变量 |
|
console - 允许服务使用控制台(已废弃,Android 10+移除) |
|
logd - 重定向输出到logd(替代 console ) |
|
其他重要选项 | |
重启控制 | class <name> - 指定服务类别 |
task_profiles <配置文件...> - 关联cgroup配置文件 |
|
override - 覆盖同名的已定义服务 |
|
memcg.swappiness <值> - 控制内存交换行为 |
|
reboot_on_failure <目标> - 自定义崩溃时重启目标(如 reboot_on_failure bootloader ) |
class <name> |
|
---|---|
core - 核心系统服务 | 最基本的系统服务 |
通常在早期启动阶段启动 | |
示例:ueventd(设备节点管理)、logd(日志服务) | |
main - 主要系统服务 | 系统正常运行所需的主要服务 |
在核心服务之后启动 | |
示例:servicemanager(Binder IPC)、surfaceflinger(图形合成) | |
late_start - 延迟启动服务 | 在系统基本功能就绪后启动 |
通常用于不紧急的服务或第三方服务 | |
示例:一些厂商定制服务 | |
****charger - 充电模式服务 | 仅在设备处于充电模式时启动 |
用于显示充电界面和状态 | |
示例:charger服务 | |
boot - 启动类服务 | 与系统启动过程相关的服务 |
示例:bootanim(启动动画) | |
****post-boot - 启动后服务 | 在系统完成主要启动后运行 |
用于优化或维护任务 | |
****hal - 硬件抽象层服务 | 与硬件交互的服务 |
示例:各种HIDL/ AIDL HAL服务 | |
****apex - APEX模块服务 | Android APEX模块提供的服务 |
****vnd - 供应商特定服务 | 设备制造商提供的专有服务 |
**early_hal` - 早期HAL服务 | 需要在其他服务之前启动的HAL服务 |
6、核心rc文件解析 (/system/core/rootdir/init.rc)
#配置一些必要的环境变量,导入硬件驱动服务
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc
.....
on init
.....
# 也就是上面导入的其中三个servicemanager服务,在init阶段会启动,这三个服务是binder核心管理服务
start servicemanager
start hwservicemanager
start vndservicemanager
#在late-init阶段执行fs文件系统的初始化
#early-init→ init→ late-init(early-fs→ fs→ post-fs→ post-fs-data...)
on late-init
trigger early-fs
trigger fs
trigger post-fs
trigger late-fs
trigger post-fs-data
trigger load-bpf-programs
trigger bpf-progs-loaded
##触发zygote服务
trigger zygote-start
trigger firmware_mounts_complete
trigger early-boot
trigger boot
on early-fs
##存储系统的基石,Volume Daemon 卷守护进程
start vold
on late-fs
#启动class为early_hal的所有服务
.....
class_start early_hal
.....
#上面在late-init阶段被执行
on zygote-start
wait_for_prop odsign.verification.done 1
exec_start update_verifier
start statsd
#启动64位zygote
start zygote
#启动32位zygote(忽略)
start zygote_secondary
on boot
.....
#启动所有class为hal服务
class_start hal
#启动所有class为core的服务
class_start core
#核心服务包括ueventd,这个服务和init程序是同一个程序,只不过拷贝到了/system/bin/ueventd下,实际调用/system/core/init/main.cpp中的ueventd_main(argc,argv)分支,该守护进程负责设备节点管理,以及后面的设备热插拔管理, 它是android硬件访问的基石
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
user root
shutdown critical
#在late-init阶段,zygote进程被启动
#app_process64源码路径(/frameworks/base/cmds/app_process/app_main.cpp)
#传入了6个参数
#-Xzygote \ # argv[1]:虚拟机参数
#/system/bin \ # argv[2]:父目录路径
#--zygote \ # argv[3]:标志位(启动zygote模式)
#--start-system-server \ # argv[4]:标志位(启动系统服务)
#--socket-name=zygote # argv[5]:指定socket名称
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main #class类型是main
priority -20 #优先级-20
user root #以root用户启动
group root readproc reserved_disk #以root readproc reserved_disk用户组
socket zygote stream 660 root system #socket流的权限
socket usap_pool_primary stream 660 root system
#服务重启时执行vdc,并重启audioserver、cameraserver、media、netd等进程
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart write /sys/power/wake_lock zygote_kwl
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
#为 Zygote 进程分配高性能资源策略,确保其孵化的应用进程能获得充足的系统资源
task_profiles ProcessCapacityHigh MaxPerformance
#定义 Zygote 进程的崩溃监控策略,防止频繁崩溃导致系统不稳定。
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
后面就正式进入android framework了。