前言
Android系统最早时伴随着手机出现的,但是由于Android系统开源且系统生态成熟,近几年新兴起的智能电视、智能家居、新能源汽车等行业,基本上也都选择了Android系统作为设备软件载体,这就导致虽然Android应用开发的热度降低了,但是Android系统特别开发热度却越来越高。由于不同设备的应用场景不同,这就导致无法直接使用Android系统,而需要根据自己的业务场景来具体定制Android系统以满足自己的需求。而想要特殊化定制Android系统,基本都绕不开Framework的内容,而想要深入理解Android系统的Framework层源码,第一步自然就是理解Android系统的启动流程,本篇文章我们将会结合Android9.0系统源码来具体来分析以下Android系统的启动流程。
了解Framework的同学,可能都或多或少的知道AMS、WMS、类加载、热修复Sophix、插件化等都要涉及系统的启动流程。
一、按下Android设备电源
1、启动电源及系统,按下电源键之后,引导芯片开始将固化在ROM中的BootLoader加载至RAM中,并执行该程序。
2、加载引导程序Boot Loader到RAM,Boot Loader一般就会由厂商进行定制。
3、执行引导程序:主要作用是把系统OS拉起并运行,初始化堆栈、硬件、网络内存等操作。
4、启动Android系统的Linux内核,寻找init关键文件。
5、Kernel启动后,在用户空间启动init进程,init进程是Android系统的第一个进程,其进程号为1,该进程启动后,主要处理一些重要的初始化工作,比如创建Zygote和各种属性服务。
二、init进程的入口函数
在Linux内核加载完成后,开始查找init.rc文件,并启动init进程,其主要代码如下:
int main(int argc, char** argv) {
...
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
//清理 umask.
umask(0);
//创建和挂载启动所需的文件目录
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
// 初始化Kernel日志
InitKernelLogging(argv);
...
}
...
//属性服务初始化
property_init();//01
...
//创建epoll句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
...
//设置子进程的信号处理函数,如果子进程(Zygote进程)异常退出,init进程会调用该函数中设定的信号函数来进行处理
signal_handler_init();//02
//导入默认的环境变量
property_load_boot_defaults();
export_oem_lock_status();
//启动属性服务
start_property_service();//03
set_usb_controller();
...
if (bootscript.empty()) {
//解析init.rc配置文件
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
...
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
//重启死去的进程
restart_processes();//05
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
该流程分析如下:
- 挂载文件:通过设置umask值,屏蔽一些权限后,开始挂载和创建所需的文件;
- 属性服务:通过property_init()属性服务初始化,调用start_property_service()启动属性服务;
- 子进程的处理函数: signal_handler_init()主要作用是防止出现僵尸进程,子进程在暂停和终止后,会发出SIGCHLD的信号,signal_handler_init()接收到该信号后,会对该进程进行回收处理,防止占用系统进程资源;
- 解析init.rc配置:在8.0中对init.rc文件进行了拆分,可查看system\core\rootdir目录,包括:
init.zygote32.rc:Zygote对应的执行程序是app_process(纯32位模式);
init.zygote64.rc:Zygote对应的执行程序是app_process64(纯64位模式);
init.zygote32_64.rc:启动两个Zygote进程( zygote 和 zygote_secondary),对应的执行程序是app_process32(主模式)和app_process64;
init.zygote64_32.rc:启动两个Zygote进程( zygote 和 zygote_secondary),对应的执行程序是app_process64(主模式)和app_process32。
三、init函数解析
/system/core/rootdir/init.rc
init.rc是一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,这种语言主要包含5种类型语句:Action、Command、Service、Option和Import。init.rc的配置代码如下所示: