目录
  1. 1. Android 整体系统框架
    1. 1.1. Linux Kernel
    2. 1.2. HAL(Hardware Abstract Layer)
    3. 1.3. Android Runtime
    4. 1.4. Native C/C++ 库
    5. 1.5. Java framework
    6. 1.6. Application
    7. 1.7. 小结
  2. 2. “万物生” - init 函数
    1. 2.1. init 概述
    2. 2.2. init 分析
      1. 2.2.1. 信号处理
    3. 2.3. 小结
  3. 3. 附上 init main 函数源码
重拾Android-【吃透源码系列】之Android系统启动(一)信号处理

在了解整个 Android 系统启动前,我们应先熟悉 Android 系统的整个架构,否则,针对某一层去进行深入理解找不到方向,容易产生“厌码症”。

这里指出文中查看源码均是基于Android 9版本,所用工具是 SourceInsight

Android 整体系统框架

Android源码架构.png

Linux Kernel

Android 平台的基础是 Linux 内核。这一层包含 Linux 内核以及一些驱动模块(比如 USB 驱动,Camera 驱动 等等)。

HAL(Hardware Abstract Layer)

硬件抽象层提供标准界面,向更高级别的 Java API 框架显示设备硬件功能。HAL 包含多个模块,其中每个模块均由特定类型的硬件组件实现界面,例如 BlueTeath、Camera等等。当框架 API 要求访问设备硬件时,Android 系统降为该硬件组件加载对应模块。

Android Runtime

对于运行 Android 5.0 (API LEVEL = 21)或更高设备,每个应用都在其自己的进程中运行,并且有它们各自的Android Runtime(ART)示例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机,DEX 文件是一种转为 Android 设计的字节码格式文件,经过 optimize,使用的内存更少。编译工具链(例如 Jack)将 Java 源代码编译成 Android dex 字节码。替换之前的 Android 工具链,即由 javac, proGrard, javajardx 多个工具组成的集合,使其可在 Android 平台上运行。

ART的部分主要功能有:

  • 预先(AOT)和即时(JIT)编译

  • 优化的垃圾回收(GC)机制

  • 更好的调试支持,包括:专用采样分析器、详细的诊断异常和奔溃报告,并且能够设置监视点以及监控特定字段。

但是在 API21之前,Android 系统采用的是 Dalvik 虚拟机来作为 Android runtime的。

Native C/C++ 库

Android 大部分核心系统组件和服务(比如 ART 、HAL)均是由其原生代码构建的,所以也就需要 C/C++ 编写的原生库。Android 平台提供 Java 框架 API,以此向应用层提供可使用的部分原生库功能。例如:可以通过 Android 框架的 Java OpenGL API 访问 OpenGL ES,以支持在应用中绘制和操作 2D 和 3D 图形。

Java framework

我们可以通过 Java 语言编写的 API 使用 Android OS 的整个功能集。这些 API 构成了 Android 应用所需的各种构件模块,他们可以简化核心模块化系统组件和服务的重复使用。

Application

系统附带的一些常用的核心应用,比如:电子邮件、短信、日历、浏览器、联系人等等。

小结

介绍完系统架构,接下来就要先从 Native 层开始看起了,这里面涉及到很多核心的 Android 系统组件和服务,源码下载可以选择AOSP,这里附上清华大学镜像网站以供下载学习。

官方源码文档有这么一句话

init.rc is the primary .rc file and is loaded by the init executable at the beginning of its execution.
It is responsible for the initial set up of the system.

既然 init 函数负责系统的初始化,那么就从这个入口开始吃透 Android 系统启动流程。

“万物生” - init 函数

init 概述

init 作为 Linux 系统(也是 Android 系统)中用户空间的第一个进程,其进程号是为1,作为第一号进程,它被赋予了很多极其重要的工作责任,其中之一就是负责创建系统中的关键进程,尤其是 zygote。它是 Java 维度世界的缔造者。那么接下来就开始介绍 init 如何创建 zygote。会很长,慢慢来…

init 分析

由于源码 main 函数略长,这里先详述 main 方法中的4大核心知识点之一:信号处理,后面会分篇章去介绍:rc文件语法、启动服务 以及 属性服务。

信号处理

sigchld_handler_init();

// system/core/init sigchld_handler.cpp
void sigchld_handler_init() {
// 为 SIGCHLD 创建一个信令机制。
int s[2];

// socketpair() 创建一对未命名、相互连接的 UNIX 域套接字
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
}

signal_write_fd = s[0];
signal_read_fd = s[1];

// 如果捕获 SIGHLD 信号,就将其写入 signal_write_fd 文件句柄
struct sigaction act;
memset(&act, 0, sizeof(act));

// 设置信号处理函数句柄,当有信号产生时,会向上面创建的 socket 写入数据,
// 当 epoll 监控到该socketpair中的fd可读时,就会调用注册的函数取处理该事件
act.sa_handler = SIGCHLD_handler;

// SA_NOCLDSTOP 使 init 进程只有在其子进程终止时才会收到 SIGCHLD 信号
act.sa_flags = SA_NOCLDSTOP;

// 初始化 SIGCHLD 信号处理方式
sigaction(SIGCHLD, &act, 0);

// 处理这之前退出的子进程:调用 waitpid() 等待子进程退出,waitpid() 会拿到一个 siginfo,这个 siginfo 中有退出的子进程的 pid
ReapAnyOutstandingChildren();

// 注册 epoll 机制
register_epoll_handler(signal_read_fd, handle_signal);
}

最后通过 epoll_ctl() 向 epoll_fd 注册本地 socket,监听其是否可读;然后注册了 epoll 事件的处理函数

void register_epoll_handler(int fd, void (*fn)()) { 
epoll_event ev;

//对文件描述符可读
ev.events = EPOLLIN;

//保存指定的函数指针,用于后续的事件处理
ev.data.ptr = reinterpret_cast<void*>(fn);

//向 epoll_fd 添加要监听的fd,比如property、keychord和signal事件监听
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
ERROR("epoll_ctl failed: %s\n", strerror(errno));
}
}

这里说明一下,每个进程在处理其他进程发送的 signal 信号时都需要先注册,当进程的运行状态改变或者终止时,会产生某个 signal,init 进程是所有用户空间进程的父进程,当其子进程终止时产生信号,传递参数给 sigaction 结构体,便完成信号处理的过程。

这里在看一下 信号写入与读取 两个函数:

// signal 读取
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));

ReapAnyOutstandingChildren();
}

// signal 写入
static void SIGCHLD_handler(int) {
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
PLOG(ERROR) << "write(signal_write_fd) failed";
}
}

继续看 ReapAnyOutstandingChildren() 函数,这又是很长的…

void ReapAnyOutstandingChildren() {
while (ReapOneProcess()) {
}
}

static bool ReapOneProcess() {
siginfo_t siginfo = {};
// 这里会返回一个僵尸进程(僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。)
// 或者通知我们已经没有僵尸进程遗留
// 这里不接收进程,如下所示
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
PLOG(ERROR) << "waitid failed";
return false;
}

auto pid = siginfo.si_pid;
if (pid == 0) return false;

// 这时我们知道我们有一个僵尸进程, 所以在此之前无论何时返回我们都使用 scopeguard 来回收这个进程
// 我们不想要过早的回收僵尸进程 as in Service::Reap(), we kill(-pid, ...)
// 并且我们希望进程在整个使用过程中(以及将来的使用过程中)仍然保持有效
auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });

std::string name;
std::string wait_string;
Service* service = nullptr;

if (PropertyChildReap(pid)) {
name = "Async property child";
} else if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
service = ServiceList::GetInstance().FindService(pid, &Service::pid); // ------------------------------------- (1)

if (service) {
name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
if (service->flags() & SVC_EXEC) {
auto exec_duration = boot_clock::now() - service->time_started();
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
}
} else {
name = StringPrintf("Untracked pid %d", pid);
}
}

if (siginfo.si_code == CLD_EXITED) {
LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
} else {
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}

if (!service) return true;

service->Reap(siginfo); // ------------------------------------- (2)

if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service);
}

return true;
}

(1)这之后会根据这个 pid 从 ServiceList 中找到这个退出的子进程的 Service 对象;

(2)然后调用这个 Service 对象的 Reap()函数;

再看 Reap 函数

// system/core/init Service.cpp
void Service::Reap(const siginfo_t& siginfo) {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL); // -------------------------------- (1)
}

// Remove any descriptor resources we may have created.
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));

for (const auto& f : reap_callbacks_) {
f(siginfo);
}

if (flags_ & SVC_EXEC) UnSetExec(); // -------------------------------- (2)

if (flags_ & SVC_TEMPORARY) return;

pid_ = 0;
flags_ &= (~SVC_RUNNING); // -------------------------------- (3)
start_order_ = 0;

// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
flags_ |= SVC_DISABLED;
}

// Disabled and reset processes do not get restarted automatically.
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
return;
}

// If we crash > 4 times in 4 minutes, reboot into recovery.
boot_clock::time_point now = boot_clock::now();
if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
if (now < time_crashed_ + 4min) {
if (++crash_count_ > 4) {
LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
}
} else {
time_crashed_ = now;
crash_count_ = 1;
}
}

flags_ &= (~SVC_RESTART);
flags_ |= SVC_RESTARTING;

// Execute all onrestart commands for this service.
onrestart_.ExecuteAllCommands(); // -------------------------------- (4)

NotifyStateChange("restarting");
return;
}

(1)首先调用 KillProcessGroup(SIGKILL),这个函数会调用 processgroup.cpp 中的 KillProcessGroup():目的是kill掉退出的子进程的所有子进程。

(2)调用 UnsetExec() 将 is_exec_service_running_ 设置为false并将 SVC_EXEC flag清掉;

(3)将 flags_ 中的SVC_RESTARTING flag 赋值

(4)调用 onrestart_.ExecuteAllCommands():执行此服务的所有 onrestart 命令。

在 init main函数死循环中,会不断调用 Service::is_exec_service_running() 函数,这个函数是直接 return Service 对象的 is_exec_service_running_ 静态布尔变量。因为上面第(2)步有将 is_exec_service_running_ 设置为false,所以这个if条件不命中,就会调用 RestartProcessed() 函数。在这个函数中,会从 ServiceList 中取出每一个 Service 对象,然后判断其 flags_ 遍历中是否有 SVC_RESTARTING flag,如果有,最终会调到这个 service 的 Start() 去再次启动这个service。

小结

综上,大致可以捋清楚 Android Init进程对信号的处理流程

在 Android 中,当一个进程 exit() 退出时,会向它的父进程发送一个 SIGCHLD 信号。父进程收到该信号后,会释放分配给该子进程的系统资源;并且父进程需要调用 wait() 或者 waitpid() 等待子进程结束。如果父进程没有做这种处理,且父进程初始化时也没有调用 signal(SIGCHLD, SIG_IGN) 来忽略对 SIGCHLD 的处理,那么子进程将一直保持当前的退出状态,不会完成退出。但是这样的子进程不会被调度,所表现出的只是在进程列表中占据一个位置,保存了该进程的一些 siginfo,比如:进程的PID、终止状态、CPU调度使用时间等信息;类似于对于这种进程又称之为 “Zoombie”进程。

在 Linux 中,设置僵尸进程的目的是维护子进程的一些信息,以供父进程后续查询获取该进程之用。特殊的,如果一个父进程终止,那么它的所有僵尸子进程的父进程就会被设置为 Init进程(PID为1),并由 Init 进程负责回收这些僵尸进程(Init进程将 wait() / waitpid() 这些进程,并清除它们在进程列表中的信息)。

由于僵尸进程仍然会占据进程列表中的位置,而 Linux 所支持的最大进程数量是有限的,超过这个阈值,系统就无法创建进程。所以,是很有必要清除那些僵尸进程的,以保证系统的正常操作,而这也是 native 层 Init进程 在对信号处理上所赋予的权力所在。

附上 init main 函数源码

最后附上源码注解。

#include "sigchld_handler.h"
#include "ueventd.h"
#include "watchdogd.h"
#include "log.h"

// system/core/init Init.cpp
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}

if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}

if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}

if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}

// 以上四个条件返回均在声明文件里才能找到相关命中条件操作,跟旧版本有差,具体查找见 #include 并查看相应的cpp文件

// 进入第一阶段
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();

/* Clear the umask. -- Linux umask命令指定在建立文件时预设的权限掩码 */
umask(0); // 设置创建新文件时的权限遮罩

clearenv(); // 清除所有的环境变量
setenv("PATH", _PATH_DEFPATH, 1); // 设置环境变量

/* 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. -- 创建文件系统
*/
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));

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));

if constexpr (WORLD_WRITABLE_KMSG) {
mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
}

mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));


mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000");
mkdir("/mnt/vendor", 0755);

// tmpfs已安装在/dev上,并且有了/dev/kmsg,这样就可以和外面的世界“交流”起来啦……
InitKernelLogging(argv);

// ☆☆☆☆☆☆☆☆☆☆☆ 以上是一些挂载文件操作 ☆☆☆☆☆☆☆☆☆☆☆
// 在linux操作系统中, 挂载是指将一个设备(通常是存储设备)挂接到一个已存在的目录上。
// 我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的目录上, 然后通过访问这个目录来访问存储设备。

LOG(INFO) << "init first stage started!";

if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}

SetInitAvbVersionInRecovery();

// 如果全局启动项通过则启用 securecomputing mode(一种沙箱安全机制)
// 否则没通过就交给 zygote 启用
// 在Linux系统里,大量的系统调用(systemcall)直接暴露给用户态程序。
// 但是,并不是所有的系统调用都被需要,而且不安全的代码滥用系统调用会对系统造成安全威胁。
// 通过seccomp,我们限制程序使用某些系统调用,这样可以减少系统的暴露面,同时是程序进入一种“安全”的状态。
global_seccomp();

// 安装 SELinux, 加载SELinux策略:指定进程可以访问那些文件
SelinuxSetupKernelLogging();
SelinuxInitialize();

// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /init failed";
}

setenv("INIT_SECOND_STAGE", "true", 1);

static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

char* path = argv[0];
char* args[] = { path, nullptr };
execv(path, args);

// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
}

// 此时处于init的第二阶段:属性服务相关是Android系统中另一个比较重要的模块,会在另一篇中详述
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";

// 设置所有进程都可以访问的会话密匙环。它将保存FBE加密密钥之类的东西。任何进程都不应覆盖其会话密钥环。
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

// 指示正在引导到后台fw加载器,等等。
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// 初始化属性服务
property_init();

// 如果参数同时在命令行和DT中传递,DT中设置的属性总是优先于命令行属性。
process_kernel_dt();
process_kernel_cmdline();

// 将内核变量传播到内部变量,用于init以及当前所需的属性。
export_kernel_boot_props();

// init 的启动时间提供给 bootstat 进行日志记录
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

// 设置 libavb 匹配第三代构建版本 Framework-only OTA(over the air)
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);

// 清除我们的环境变量
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");

// 现在为第二阶段:设置 SELinux
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();

// 创建 epoll 文件句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(FATAL) << "epoll_create1 failed";
}

// 信号处理
sigchld_handler_init();

if (!IsRebootCapable()) {
// 如果 init 没有 CAP_SYS_BOOT 功能, 那么它就会在容器中运行.
// 在这种情况下, 接收 SIGTERM 将导致系统关闭。
InstallSigtermHandler();
}

// 基操:加载属性服务默认启动设置、导入oem锁状态 etc.
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
set_usb_controller();

const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);

subcontexts = InitializeSubcontexts();

ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();

LoadBootScripts(am, sm);

// 打开这个选项并丢弃信息日志记录将增加0.2秒 -- 为了节省0.2秒,this is 细节啊!
// Nexus 9启动时间,因此默认禁用。
if (false) DumpState();

am.QueueEventTrigger("early-init");

// 一个等待冷启动完成的操作队列,这样我们就能知道 ueventd 已经设置了所有的/dev…
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... 这样我们就可以开始对需要来自/dev的内容的操作进行排队。
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");

// 触发所有的启动操作,让我们开始吧
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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");

// 不要在充电器模式下安装文件系统或启动核心系统服务。
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}

// 根据属性的运行状态运行所有的属性触发器
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

while (true) {
// 默认情况下,一直休眠状态,知道触发某些事情
int epoll_timeout_ms = -1;

if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}

// 循环遍历事件队列,直到有操作服务需要执行
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_restart_time = RestartProcesses();

// 如果有某个过程需要重新启动,及时停止休眠状态
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
}

// 如果还有其他的工作需要做, 立马再次醒来
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;
}
打赏
  • 微信
  • 支付宝

评论