本文基于Android 12介绍Linux Kernel层和init进程的启动过程。
一、Android启动过程概述
Android正常模式启动过程如下图所示:
- 启动电源:当按下电源键时,引用芯片从预定义的地方开始执行,加载引导程序BootLoader到RAM,开始执行。
- BootLoader:它是在操作系统内核运行之前运行的一段小程序,初始化硬件设备、建立内存空间映射图,从而将OS拉起来。
- Linux Kernel:它启动后,设置缓存、计划列表、加载驱动,启动init进程。
- init:初始化和启动PropertyService,启动zygote和servicemanager进程。
- zygote:c++层:启动VM并为VM注册JNI方法,调用ZygoteInit进入Java层;Java层:预加载类、库等各种资源,创建服务器端Socket,启动systemserver进程。
- Launcher:在SystemServer.startOtherService()中的AMS.systemReady()中启动Launcher。
二、Linux Kernel启动过程
Android 12源码中没有相关Kernel的代码,以下是参考《Android的设计与实现 卷I》和项目上kernel代码总结。
Kernel启动分为两个阶段:
(1)内核引导阶段。通常使用汇编语言,代码路径为:kernel/msm-4.19/arch/arm/kernel/head.S和
kernel/msm-4.19/arch/arm/kernel/head-common.S。
(2)内核启动阶段。引导阶段调用start_kernel()进入启动阶段,代码路径在:/kernel/msm-4.19/init/main.c。Kernel启动init过程如下:
三、init进程启动过程
init进程是Linux Kernel用户空间的第一个进程,Pid=1。代码路径为: /system/core/init/,他的入口为main.cpp,它的main()方法会多次进入,源码参考地址为:main.cpp - OpenGrok cross reference for /system/core/init/main.cpp。
1. init进程启动的FirstStageMain阶段
在FirstStageMain阶段,主要的工作如下:
- umask(0):清除系统默认权限,保证新建目录的访问权限由mkdir设置。
- 使用mkdir/mount/chmod指令来创建基本文件系统目录并挂载相关的文件系统。Android系统挂载了tmpfs、devpts、proc、sysfs这四类文件系统。其中,/dev是设备目录,所有外部设备和虚拟设备都在该目录下;/proc是存储当前系统内核运行信息的文件目录,/sys存储了硬件设备在内核上的映射。
- SetStdioToDevNull():关闭/stdin/stdout/stderr的fd,重定向到/dev/null。
- InitKernelLogging():初始化kernel日志,输出到/dev/kmsg。
- load内核各模块。
- 第一阶段做完后,再次调用main()进入SetupSelinux阶段。
2. init进程启动的SetupSelinux阶段
Selinux启动完后会再次携带参数调用main()进入SecondStageMain阶段。
3. init进程启动的SecondStageMain阶段
在FirstStageMain阶段,主要的工作如下:
- SetStdioToDevNull():关闭/stdin/stdout/stderr的fd,重定向到/dev/null。
- InitKernelLogging():初始化kernel日志,输出到/dev/kmsg。
- 创建会话秘钥。
- 设置init进程和其fork()的子进程的oom_adj。oom_adj主要用在lowmemorykiller机制中,每一类别的进程会有其oom_adj的范围,oom_adj值越大表示进程越不重要,当lowmemory时,会优先kill oom_adj高的进程。
- PropertyInit(),初始化属性服务。
初始化属性服务的逻辑:
1. mkdir "/dev/__properties"目录;
2. createSerializedPropertyInfo()读取context信息并将内容序列化写入 /dev/__properties__/ property_info;
3. ProcessKernelDt()读取设备树DT;
4. ProcessKernelCmdline()加载proc/cmdline,把"androidboot."开头的替换成ro.boot并设置新值。
5.ProcessBootconfig()加载/proc/bootconfig,把"androidboot."开头的替换成ro.boot并设置新值。
6. ExportKernelBootProps()将kernel变量传播到init可用的变量和其当前可用的属性。
7. PropertyLoadBootDefaults()加载开机默认的属性
- StartPropertyService:启动属性服务。
- InitializeSubcontext:初始化SubContext。
- 获取ActionManager实例和ServiceList实例
- LoadBootScrpts:加载和解析init.rc文件。
- 把Action和Trigger添加到ActionManager的相应队列中。
- 无限循环执行init.rc中的command。
init.rc中可以看到在触发“init”,会启动logd、servicemanager、hwservicemanager等。
on init
sysclktz 0 //将时区设置为0
...
# Start logd before any other services run to ensure we capture all of their logs.
start logd
# Start essential services.
start servicemanager
start hwservicemanager
start vndservicemanager
触发 late-init,会启动各种fs,Zygote、early-boot、boot等。
# Mount filesystems and start core system services.
on late-ini
trigger early-fs
trigger fs
trigger post-fs.
trigger late-fs
trigger post-fs-data
trigger load_persist_props_action
trigger load_bpf_programs
#触发启动zygote
trigger zygote-start
trigger firmware_mounts_complete
trigger early-boot
trigger boot
on early-fs
# Once metadata has been mounted, we'll need vold to deal with userdata checkpointing
start vold
on post-fs
exec - system system -- /system/bin/vdc checkpoint markBootAttempt
mount rootfs rootfs / remount bind ro nodev
# Mount default storage into root namespace
mount none /mnt/user/0 /storage bind rec
mount none none /storage slave rec
chown system cache /cache
chmod 0770 /cache
restorecon_recursive /cache
mkdir /cache/recovery 0770 system cache
chown root system /proc/kmsg
chmod 0440 /proc/kmsg
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
wait_for_prop odsign.verification.done 1
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
start netd
start zygote
start zygote_secondary
所以servicemanager进程比zygote启动更早。