一.Android启动简介
Android 是一种基于 Linux 的开放源代码软件栈,为广泛的设备和机型而创建。下图所示为 Android 平台的主要组件。
Linux 内核
Android 平台的基础是 Linux 内核。例如,Android Runtime (ART) 依靠 Linux 内核来执行底层功能,例如线程和低层内存管理。
- 音频驱动(Audio):常用的基于ALSA的高级Linux声音体系驱动。
- Binder IPC驱动:Android的一个特殊的驱动程序,具有单独的设备节点,提供进程间通信的功能。
- 显示驱动(Display):基于Linux的帧缓冲(Frame Buffer)驱动。
- 键盘驱动(KeyBoard):作为输入设备的键盘驱动。
- 蓝牙驱动(Bluetooth Driver):基于IEEE 802.15.1标准的无线传输技术。
- 照相机驱动(Camera Driver):常用的基于Linux的v412(Video for Linux)的驱动。
- Flash内存驱动(Flase Memory Driver):基于MTD的Flash驱动程序。
- WiFi驱动:基于IEEE 802.11标准的驱动程序。
HAL(Hardware Abstraction Layer)
硬件抽象层 (HAL) 提供标准界面,向更高级别的 Java API 框架显示设备硬件功能。HAL 包含多个库模块,其中每个模块都为特定类型的硬件组件实现一个界面,例如相机或蓝牙模块。当框架 API 要求访问设备硬件时,Android 系统将为该硬件组件加载库模块。
硬件抽象层 (HAL) 会定义一个标准接口以供硬件供应商实现,并允许 Android 忽略较低别的驱动程序实现。借助 HAL,可以顺利实现相关功能,而不会影响或无需更改更高级别的系统。HAL 实现会被封装成模块 (.so) 文件,并会由 Android 系统适时地加载。
标准的HAL结构中,HAL借口包含两个通用的组件:一个模块和一个设备。
- 模块:表示被封装切存储为共享库(.so file)的实现。里面包含模块版本等元数据。
- 设备:提取产品的实际硬件。例如,音频模块可能会包含主音频设备、USB 音频设备或蓝牙 A2DP 音频设备。
Android Runtime
Android Runtime时运行在Android 5.0(API 级别 21)或更高版本的设备上。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机,DEX 文件是一种专为 Android 设计的字节码格式,经过优化,使用的内存很少。编译工具链(例如 Jack)将 Java 源代码编译为 DEX 字节码,使其可在 Android 平台上运行。
ART 的部分主要功能包括:
- 预先 (AOT) 和即时 (JIT) 编译
- 优化的垃圾回收 (GC)
- 更好的调试支持,包括专用采样分析器、详细的诊断异常和崩溃报告,并且能够设置监视点以监控特定字段
原生 C/C++ 库 :
许多核心 Android 系统组件和服务(例如 ART 和 HAL)构建自原生代码,需要以 C 和 C++ 编写的原生库。Android 平台提供 Java 框架 API 以向应用显示其中部分原生库的功能。
- OpenGl ES: Android包括支持高性能2 d和3 d图形与开放图形库。OpenGL是一个跨平台的图形API,它为3D图形处理硬件指定了一个标准的软件接口。
- WebKit:一个开源的浏览器引擎.
- OenMAX AL:一个多媒体应用程序的标准,基于 Khronos Group OpenMAX AL™ 1.0.1 standard,在Android4.0以上使用。
- Libc:从BSD继承来的标准C系统函数库,专门为基于embedded linux的设备定制。
Application Framework(应用框架层)
- View System:可用以构建应用的 UI,包括列表、网格、文本框、按钮甚至可嵌入的网络浏览器
- Content Providers:它可以让一个应用访问另一个应用的数据,或共享它们自己的数据。
- Resource Manager:用于访问非代码资源,例如本地化的字符串、图形和布局文件 。
- Notification Manager:可让所有应用在状态栏中显示自定义提醒 。
- Activity Manager:用于管理应用的生命周期,提供常见的导航返回栈
- Window Manager:管理所有的窗口程序。
- Package Manager:Android系统内包的程序管理。
System Apps
Android 随附一套用于电子邮件、短信、日历、互联网浏览和联系人等的核心应用。平台随附的应用与用户可以选择安装的应用一样,没有特殊状态。因此第三方应用可成为用户的默认网络浏览器、短信 Messenger 甚至默认键盘(有一些例外,例如系统的“设置”应用)。
其实分析了这张图都有什么东西,但是对于安卓系统启动流程还是很模糊的。我不打算循序渐进,先放一张图,来对我这篇文章中提到的安卓系统流程有个整体的了解。
二、Linux内核启动
开机按Power,正常启动系统,加载boot.img,boot.img包含内核,基本文件系统,用于正常启动手机。
1.引导程序Bootloader
BootLoader是在操作系统运行之前运行的一段程序,它可以将系统的软硬件环境带到一个合适状态,为运行操作系统做好准备。它比较像电脑上的BIOS,它就是要把OS拉起来运行。Bootloader有三种模式如下:
a:开机后按组合键启动到fastboot模式,即命令或SD卡烧写模式,不加载内核及文件系统,可以通过数据线与电脑连接,然后在电脑上执行一些命令,如刷系统镜像到手机上。
b:开机后按组合键启动到recovery模式,加载recovery.img,recovery.img包含内核,部分文件系统,可以读sdcard中的update.zip进行刷机,也可以清除cache和用户数据。
c:开机按Power,正常启动系统,加载boot.img,boot.img包含内核,基本文件系统,用于正常启动手机。
正常启动流程,也就是c流程。
Bootloader做的事情主要有:初始化CPU时钟,内存,串口等;设置linux启动参数;加载Linux各种各样的内核镜像到SDRAM中。
2.linux内核启动
内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。
三、Init进程
Init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。init始终是第一个进程.
1.Init入口函数
system/core/init/init.cpp
init的入口函数为main。
int main(int argc, char** argv) {
...
// Get the basic filesystem setup we need put together in the initramdisk on / and then we'll let the rc file figure out the rest.
//基本的系统的设置。挂载信息,创建一些文件夹。从注释中我们可以看到,它把一些其他的操作放到了rc文件下。之后会提到。
if (is_first_stage) {
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));
mount("sysfs", "/sys", "sysfs", 0, NULL);
}
//重定向标准输入\输出到 dev/_null_
open_devnull_stdio();
//初始化内核log系统
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
if (!is_first_stage) {
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
//初始化属性相关资源,利用ashmem共享。
property_init();
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
}
// Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
//设置SELinux policy策略。
selinux_initialize(is_first_stage);
if (is_first_stage) {
if (restorecon("/init") == -1) {
ERROR("restorecon failed: %s\n", strerror(errno));
security_failure();
}
char* path = argv[0];
char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
if (execv(path, args) == -1) {
ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
security_failure();
}
}
NOTICE("Running restorecon...\n");
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon("/property_contexts");
restorecon_recursive("/sys");
//创建epoll句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
ERROR("epoll_create1 failed: %s\n", strerror(errno));
exit(1);
}
signal_handler_init();
property_load_boot_defaults();
export_oem_lock_status();
//启动属性服务
start_property_service();
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
//解析init.rc。
parser.ParseConfig("/init.rc");
//解析完成配置文件之后,会得到一系列的action动作。init和boot。
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = property_get("ro.bootmode");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
//while循环不断调用ExecuteOneCommand函数时,匹配的action。
if (!waiting_for_exec) {
am.ExecuteOneCommand();
restart_processes();
}
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (am.HasMoreCommands()) {
timeout = 0;
}
bootchart_sample(&timeout);
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
//通过epoll来监听,处理属性服务相关的事情。
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
我们对上面的代码进行总结:
1.创建文件系统目录并挂载相关的文件系统。
2.初始化内核log系统。
3.初始化属性相关资源,利用ashmem共享。
4.设置SELinux policy策略
- Android 使用 SELinux 对所有进程强制执行强制访问控制 (MAC),其中包括以 Root/超级用户权限运行的进程(也称为 Linux 功能)。SELinux 能够限制特权进程并能够自动创建安全政策,从而可提升 Android 的安全性。
- 关于SELinux 中有详细的介绍。(https://source.android.com/security/selinux/)。需要翻墙。
5.创建epoll句柄。
- epoll仅仅是一个异步事件的通知机制,其本身并不作任何的IO读写操作,它只负责告诉你是不是可以读或可以写了,而具体的读写操作,还要应用程序自己来完成。
- 该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的fd上是否发生的事件。size就是你在这个epoll fd上能关注的最大fd数,这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就会占用一个fd值,在Linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
6.启动属性服务。
//system/core/init/property_service.cpp
void start_property_service() {
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
listen(property_set_fd, 8);
register