init进程,它是一个由内核启动的用户级进程,当Linux内核启动以后,运行的第一个进程是init,这个进程是一个守护进程,确切的说,它是Linux系统中用户控件的第一个进程,因此它的进程号是1。它的生命周期贯穿整个linux 内核运行的始终, linux中全部其它的进程的共同始祖均为init进程。html
关键类 | 路径 |
---|---|
init.rc | system/core/rootdir/init.rc |
init.cpp | system/core/init/init.cpp |
property_service.cpp | system/core/init/property_service.cpp |
init_parser.h | system/core/init/init_parser.h |
init_parser.cpp | system/core/init/init_parser.cpp |
log.cpp | system/core/init/log.cpp |
logging.cpp | system/core/base/logging.cpp |
property_service.cpp | system/core/init/property_service.cpp |
signal_handler.cpp | system/core/init/signal_handler.cpp |
service.cpp | system/core/init/service.cpp |
Action.cpp | system/core/init/Action.cpp |
builtins.cpp | system/core/init/builtins.cpp |
🏹 按下电源系统启动
当电源按下时引导芯片代码开始从预约义的地方(固化在ROM)开始执行,加载引导程序Bootloader到RAM,而后执行。 java
🏹 引导程序Bootloader
引导程序是在Android操做系统开始运行前的一个小程序,它的主要做用是把系统OS拉起来并运行。 linux
🏹 linux内核启动
内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,而后启动root进程或者系统的第一个进程。 android
🏹 init进程启动
✨ 这就是咱们接下来要讨论的内容 ✨ios
Android init进程的入口文件在system/core/init/init.cpp中,因为init是命令行程序,因此分析init.cpp首先应从main函数开始:编程
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ //根据参数,判断是否须要启动ueventd和watchdogd if (!strcmp(basename(argv[0]), "ueventd")) { // 启动ueventd return ueventd_main(argc, argv); } if (!strcmp(basename(argv[0]), "watchdogd")) { // 启动watchdogd return watchdogd_main(argc, argv); } if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); // 若紧急重启,则安装对应的消息处理器 } add_environment("PATH", _PATH_DEFPATH); // 添加环境变量 ... ... }
int main(int argc, char** argv) { /* 01. 判断及增长环境变量 */ bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); if (is_first_stage) { // 判断是不是系统启动的第一阶段(第一次进入:true) // 用于记录启动时间 boot_clock::time_point start_time = boot_clock::now(); // Clear the umask. umask(0); // 清除屏蔽字(file mode creation mask),保证新建的目录的访问权限不受屏蔽字影响 // 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. // 挂载tmpfs文件系统 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); // 挂载devpts文件系统 mount("devpts", "/dev/pts", "devpts", 0, NULL); #define MAKE_STR(x) __STRING(x) // 挂载proc文件系统 mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)); // Don't expose the raw commandline to unprivileged processes. // 8.0新增, 收紧了cmdline目录的权限 chmod("/proc/cmdline", 0440); // 8.0新增,增长了个用户组 gid_t groups[] = { AID_READPROC }; setgroups(arraysize(groups), groups); // 挂载sysfs文件系统 mount("sysfs", "/sys", "sysfs", 0, NULL); // 8.0新增 mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL); // 提早建立了kmsg设备节点文件,用于输出log信息 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)); ... ... }
如上所示,该部分主要用于建立和挂载启动所需的文件目录。须要注意的是,在编译Android系统源码时,在生成的根文件系统中, 并不存在这些目录,它们是系统运行时的目录,即当系统终止时,就会消失。 小程序
四类文件系统:缓存
tmpfs:一种虚拟内存文件系统,它会将全部的文件存储在虚拟内存中,若是你将tmpfs文件系统卸载后,那么其下的全部的内容将不复存在。tmpfs既可使用RAM,也可使用交换分区,会根据你的实际须要而改变大小。tmpfs的速度很是惊人,毕竟它是驻留在RAM中的,即便用了交换分区,性能仍然很是卓越。因为tmpfs是驻留在RAM的,所以它的内容是不持久的。断电后,tmpfs的内容就消失了,这也是被称做tmpfs的根本缘由。安全
devpts:为伪终端提供了一个标准接口,它的标准挂接点是/dev/ pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的建立一个新的pty设备文件。网络
proc:一个很是重要的虚拟文件系统,它能够看做是内核内部数据结构的接口,经过它咱们能够得到系统的信息,同时也可以在运行时修改特定的内核参数。
sysfs:与proc文件系统相似,也是一个不占有任何磁盘空间的虚拟文件系统。它一般被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,它把链接在系统上的设备和总线组织成为一个分级的文件,使得它们能够在用户空间存取。
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立并挂载相关的文件系统 */ if (is_first_stage) { ... ... // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually // talk to the outside world... InitKernelLogging(argv); ... ... } ... ...
跟踪InitKernelLogging(): system/core/init/log.cpp
void InitKernelLogging(char* argv[]) { // Make stdin/stdout/stderr all point to /dev/null. int fd = open("/sys/fs/selinux/null", O_RDWR); if (fd == -1) { // 若开启失败,则记录log int saved_errno = errno; android::base::InitLogging(argv, &android::base::KernelLogger); errno = saved_errno; PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null"; } dup2(fd, 0); // dup2函数的做用是用来复制一个文件的描述符, 一般用来重定向进程的stdin、stdout和stderr dup2(fd, 1); // 它的函数原形是:int dup2(int oldfd, int targetfd),该函数执行后,targetfd将变成oldfd的复制品 dup2(fd, 2); // 所以这边的过程其实就是:建立出__null__设备后,将0、一、2绑定到__null__设备上 // 因此init进程调用InitKernelLogging函数后,经过标准的输入输出没法输出信息 if (fd > 2) close(fd); android::base::InitLogging(argv, &android::base::KernelLogger); }
跟踪InitLogging():system/core/base/logging.cpp
// 设置KernelLogger void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { //设置logger SetLogger(std::forward<LogFunction>(logger)); SetAborter(std::forward<AbortFunction>(aborter)); if (gInitialized) { return; } gInitialized = true; // Stash the command line for later use. We can use /proc/self/cmdline on // Linux to recover this, but we don't have that luxury on the Mac/Windows, // and there are a couple of argv[0] variants that are commonly used. if (argv != nullptr) { std::lock_guard<std::mutex> lock(LoggingLock()); ProgramInvocationName() = basename(argv[0]); } const char* tags = getenv("ANDROID_LOG_TAGS"); if (tags == nullptr) { return; } // 根据TAG决定最小记录等级 std::vector<std::string> specs = Split(tags, " "); for (size_t i = 0; i < specs.size(); ++i) { // "tag-pattern:[vdiwefs]" std::string spec(specs[i]); if (spec.size() == 3 && StartsWith(spec, "*:")) { switch (spec[2]) { case 'v': gMinimumLogSeverity = VERBOSE; continue; ... ... } } LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags << ")"; } }
当须要输出日志时,KernelLogger函数就会被调用:
#if defined(__linux__) void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, const char*, unsigned int, const char* msg) { ... ... // 打开log节点 static int klog_fd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); if (klog_fd == -1) return; // 决定log等级 int level = kLogSeverityToKernelLogLevel[severity]; // The kernel's printk buffer is only 1024 bytes. // TODO: should we automatically break up long lines into multiple lines? // Or we could log but with something like "..." at the end? char buf[1024]; size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg); if (size > sizeof(buf)) { size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n", level, tag, size); } iovec iov[1]; iov[0].iov_base = buf; iov[0].iov_len = size; // 经过iovec将log发送到dev/kmsg TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1)); } #endif
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ if (is_first_stage) { ... ... // 挂载特定的分区设备 if (!DoFirstStageMount()) { LOG(ERROR) << "Failed to mount required partitions early ..."; panic(); // panic会尝试reboot } } ... ...
跟踪DoFirstStageMount():system/core/init/init_first_stage.cpp
// Mounts partitions specified by fstab in device tree. bool DoFirstStageMount() { // Skips first stage mount if we're in recovery mode. if (IsRecoveryMode()) { LOG(INFO) << "First stage mount skipped (recovery mode)"; return true; } // Firstly checks if device tree fstab entries are compatible. if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) { LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)"; return true; } // 知足上述条件时,就会调用FirstStageMount的DoFirstStageMount函数 std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create(); if (!handle) { LOG(ERROR) << "Failed to create FirstStageMount"; return false; } // 主要是初始化特定设备并挂载 return handle->DoFirstStageMount(); }
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ if (is_first_stage) { ... ... // 此处应该是初始化安全框架:Android Verified Boot // AVB主要用于防止系统文件自己被篡改,还包含了防止系统回滚的功能, // 以避免有人试图回滚系统并利用之前的漏洞 SetInitAvbVersionInRecovery(); // Set up SELinux, loading the SELinux policy. selinux_initialize(true); // 调用selinux_initialize启动SELinux ... ... } ... ...
跟踪selinux_initialize():
static void selinux_initialize(bool in_kernel_domain) { Timer t; selinux_callback cb; cb.func_log = selinux_klog_callback; // 用于打印log的回调函数 selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; // 用于检查权限的回调函数 selinux_set_callback(SELINUX_CB_AUDIT, cb); // init进程的运行是区分用户态和内核态的,first_stage运行在内核态 if (in_kernel_domain) { LOG(INFO) << "Loading SELinux policy"; // 用于加载sepolicy文件 // 该函数最终将sepolicy文件传递给kernel,这样kernel就有了安全策略配置文件,后续的MAC才能开展起来。 if (!selinux_load_policy()) { panic(); } bool kernel_enforcing = (security_getenforce() == 1); // 内核中读取的信息 bool is_enforcing = selinux_is_enforcing(); // 命令行中获得的数据 // 用于设置selinux的工做模式。selinux有两种工做模式: // 一、”permissive”,全部的操做都被容许(即没有MAC),可是若是违反权限的话,会记录日志 // 二、”enforcing”,全部操做都会进行权限检查。在通常的终端中,应该工做于enforing模式 if (kernel_enforcing != is_enforcing) { if (security_setenforce(is_enforcing)) { PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false"); security_failure(); // 将重启进入recovery mode } } std::string err; if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) { LOG(ERROR) << err; security_failure(); } // init's first stage can't set properties, so pass the time to the second stage. setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1); } else { selinux_init_all_handles(); // 在second stage调用时,初始化全部的handle } }
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ if (is_first_stage) { ... ... // 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) { // 按selinux policy要求,从新设置init文件属性 PLOG(ERROR) << "restorecon failed"; security_failure(); // 失败的话会reboot } 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); // 再次调用init的main函数,启动用户态的init进程 // execv() only returns if an error happened, in which case we // panic and never fall through this conditional. PLOG(ERROR) << "execv(\"" << path << "\") failed"; security_failure(); // 内核态的进程不该该退出,若退出则会重启 } /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ ... ... }
上面全部的源码咱们都是围绕第一阶段分析(is_first_stage),自此第一阶段结束,会复位一些信息,并设置一些环境变量,最后启动用户态的init进程,进入init第二阶段。
init进程的第二阶段仍然从main函数开始入手(继续分析main函数剩余源码)
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ // 一样进行一些判断及环境变量设置的工做 ... ... // 如今 is_first_stage 为 false 了 bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); // 这部分工做再也不执行了 if (is_first_stage) { ........... } // At this point we're in the second stage of init. InitKernelLogging(argv); // 一样屏蔽标准输入输出及定义Kernel logger LOG(INFO) << "init second stage started!"; // Set up a session keyring that all processes will have access to. It // will hold things like FBE encryption keys. No process should override // its session keyring. keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1); // 最后调用syscall,设置安全相关的值 // Indicate that booting is in progress to background fw loaders, etc. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); // 这里的功能相似于“锁” ... ... property_init(); // 初始化属性域 --> 定义于system/core/init/property_service.cpp // If arguments are passed both on the command line and in DT, // properties set in DT always have priority over the command-line ones. process_kernel_dt(); process_kernel_cmdline(); // 处理内核命令 // Propagate the kernel variables to internal variables // used by init as well as the current required properties. export_kernel_boot_props(); // Make the time that init started available for bootstat to log. property_set("ro.boottime.init", getenv("INIT_STARTED_AT")); property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK")); // Set libavb version for Framework-only OTA match in Treble build. const char* avb_version = getenv("INIT_AVB_VERSION"); if (avb_version) property_set("ro.boot.avb_version", avb_version); ... ... }
这部分代码主要的工做应该就是调用 property_init 初始化属性域,而后设置各类属性。
在Android平台中,为了让运行中的全部进程共享系统运行时所须要的各类设置值,系统开辟了属性存储区域,并提供了访问该区域的API。
跟踪property_init():system/core/init/property_service.cpp
void property_init() { if (__system_property_area_init()) { // 最终调用_system_property_area_init函数初始化属性域 LOG(ERROR) << "Failed to initialize property area"; exit(1); } }
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ // Clean up our environment. unsetenv("INIT_SECOND_STAGE"); unsetenv("INIT_STARTED_AT"); // 清除掉以前使用过的环境变量 unsetenv("INIT_SELINUX_TOOK"); unsetenv("INIT_AVB_VERSION"); ... ... }
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ // Now set up SELinux for second stage. selinux_initialize(false); selinux_restore_context(); // 再次完成selinux相关的工做
咱们发如今init进程的第一阶段,也调用了selinux_initialize函数,那么二者有什么区别?
init进程第一阶段主要加载selinux相关的策略,而第二阶段调用selinux_initialize仅仅注册一些处理器。
咱们跟下selinux_initialize():
static void selinux_initialize(bool in_kernel_domain) { Timer t; selinux_callback cb; cb.func_log = selinux_klog_callback; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); if (in_kernel_domain) { ... ... // 这边就是第一阶段的工做 } else { selinux_init_all_handles(); // 这边就是第二阶段的工做:注册处理器 } }
再来看一下selinux_restore_context():主要是按 selinux policy 要求,从新设置一些文件的属性。
// The files and directories that were created before initial sepolicy load or // files on ramdisk need to have their security context restored to the proper // value. This must happen before /dev is populated by ueventd. // 如注释所述,如下文件在selinux被加载前就建立了 // 因而,在selinux启动后,须要从新设置一些属性 static void selinux_restore_context() { LOG(INFO) << "Running restorecon..."; selinux_android_restorecon("/dev", 0); selinux_android_restorecon("/dev/kmsg", 0); selinux_android_restorecon("/dev/socket", 0); selinux_android_restorecon("/dev/random", 0); selinux_android_restorecon("/dev/urandom", 0); selinux_android_restorecon("/dev/__properties__", 0); selinux_android_restorecon("/plat_file_contexts", 0); selinux_android_restorecon("/nonplat_file_contexts", 0); selinux_android_restorecon("/plat_property_contexts", 0); selinux_android_restorecon("/nonplat_property_contexts", 0); selinux_android_restorecon("/plat_seapp_contexts", 0); selinux_android_restorecon("/nonplat_seapp_contexts", 0); selinux_android_restorecon("/plat_service_contexts", 0); selinux_android_restorecon("/nonplat_service_contexts", 0); selinux_android_restorecon("/plat_hwservice_contexts", 0); selinux_android_restorecon("/nonplat_hwservice_contexts", 0); selinux_android_restorecon("/sepolicy", 0); selinux_android_restorecon("/vndservice_contexts", 0); selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/device-mapper", 0); selinux_android_restorecon("/sbin/mke2fs_static", 0); selinux_android_restorecon("/sbin/e2fsdroid_static", 0); }
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ epoll_fd = epoll_create1(EPOLL_CLOEXEC); // 调用epoll_create1建立epoll句柄 if (epoll_fd == -1) { PLOG(ERROR) << "epoll_create1 failed"; exit(1); } ... ... }
在linux的网络编程中,很长的时间都在使用 select 来作事件触发。在linux新的内核中,有了一种替换它的机制,就是 epoll。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增加而下降效率。由于在内核中的 select 实现中,它是采用轮询来处理的,轮询的fd数目越多,天然耗时越多。
epoll机制通常使用epoll_create(int size)函数建立epoll句柄,size用来告诉内核这个句柄可监听的fd的数目。
注意这个参数不一样于select()中的第一个参数,在select中需给出最大监听数加1的值。
此外,当建立好epoll句柄后,它就会占用一个fd值,在linux下若是查看/proc/进程id/fd/,可以看到建立出的fd,所以在使用完epoll后,必须调用close()关闭,不然可能致使fd被耗尽。
上述代码使用的epoll_create1(EPOLL_CLOEXEC)来建立epoll句柄,该标志位表示生成的epoll fd具备“执行后关闭”特性。
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ /* 04. 建立epoll句柄 */ signal_handler_init(); // 装载子进程信号处理器 }
init是一个守护进程,为了防止init的子进程成为僵尸进程(zombie process),须要init在子进程在结束时获取子进程的结束码,经过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引发严重的系统问题)。
在linux当中,父进程是经过捕捉 SIGCHLD 信号来得知子进程运行结束的状况,此处init进程调用 signal_handler_init 的目的就是捕获子进程结束的信号。
咱们跟踪下signal_handler_init():
void signal_handler_init() { // Create a signalling mechanism for SIGCHLD. int s[2]; // 利用socketpair建立出已经链接的两个socket,分别做为信号的读、写端 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) { PLOG(ERROR) << "socketpair failed"; exit(1); } signal_write_fd = s[0]; signal_read_fd = s[1]; // Write to signal_write_fd if we catch SIGCHLD. struct sigaction act; memset(&act, 0, sizeof(act)); // 信号处理器对应的执行函数为SIGCHLD_handler // 被存在sigaction结构体中,负责处理SIGCHLD消息 act.sa_handler = SIGCHLD_handler; act.sa_flags = SA_NOCLDSTOP; // 调用信号安装函数sigaction,将监听的信号及对应的信号处理器注册到内核中 sigaction(SIGCHLD, &act, 0); // 用于终止出现问题的子进程 ServiceManager::GetInstance().ReapAnyOutstandingChildren(); // 注册信号处理函数handle_signal register_epoll_handler(signal_read_fd, handle_signal); }
在深刻分析代码前,咱们须要了解一些基本概念:Linux进程经过互相发送消息来实现进程间的通讯,这些消息被称为“信号”。每一个进程在处理其它进程发送的信号时都要注册处理者,处理者被称为信号处理器。
注意到sigaction结构体的sa_flags为SA_NOCLDSTOP。因为系统默认在子进程暂停时也会发送信号SIGCHLD,init须要忽略子进程在暂停时发出的SIGCHLD信号,所以将act.sa_flags 置为SA_NOCLDSTOP,该标志位表示仅当进程终止时才接受SIGCHLD信号。
接下来,咱们分步骤详细了解一下signal_handler_init具体的工做流程。
// system/core/init/signal_handler.cpp static void SIGCHLD_handler(int) { if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) { PLOG(ERROR) << "write(signal_write_fd) failed"; } }
从上面代码咱们知道,init进程是全部进程的父进程,当其子进程终止产生SIGCHLD信号时,SIGCHLD_handler将对signal_write_fd执行写操做。因为socketpair的绑定关系,这将触发信号对应的signal_read_fd收到数据。
根据前文的代码咱们知道,在装载信号监听器的最后,signal_handler_init调用了register_epoll_handler,其代码以下所示,注意传入的参数分别为signal_read_fd和handle_signal:
// system/core/init/init.cpp void register_epoll_handler(int fd, void (*fn)()) { epoll_event ev; ev.events = EPOLLIN; ev.data.ptr = reinterpret_cast<void*>(fn); // epoll_fd增长一个监听对象fd,fd上有数据到来时,调用fn处理 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { PLOG(ERROR) << "epoll_ctl failed"; } }
根据代码不难看出:当epoll句柄监听到signal_read_fd中有数据可读时,将调用handle_signal进行处理。
至此,结合上文咱们知道:当init进程调用signal_handler_init后,一旦收到子进程终止带来的SIGCHLD消息后,将利用信号处理者SIGCHLD_handler向signal_write_fd写入信息;因为绑定的关系,epoll句柄将监听到signal_read_fd收到消息,因而将调用handle_signal进行处理。
// system/core/init/signal_handler.cpp static void handle_signal() { // Clear outstanding requests. char buf[32]; read(signal_read_fd, buf, sizeof(buf)); ServiceManager::GetInstance().ReapAnyOutstandingChildren(); }
从代码中能够看出,handle_signal只是清空signal_read_fd中的数据,而后调用ServiceManager::GetInstance().ReapAnyOutstandingChildren()。
ServiceManager定义于 system/core/init/service.cpp 中,是一个单例对象:
... ... ServiceManager::ServiceManager() { } ServiceManager& ServiceManager::GetInstance() { static ServiceManager instance; return instance; } ... ... void ServiceManager::ReapAnyOutstandingChildren() { while (ReapOneProcess()) { } } ... ...
如上所示,ReapAnyOutstandingChildren函数实际上调用了ReapOneProcess。咱们结合代码,看看ReapOneProcess的具体工做。
bool ServiceManager::ReapOneProcess() { siginfo_t siginfo = {}; // This returns a zombie pid or informs us that there are no zombies left to be reaped. // It does NOT reap the pid; that is done below. //用waitid函数获取状态发生变化的子进程pid //waitid的标记为WNOHANG,即非阻塞,返回为正值就说明有进程挂掉了 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; // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid // whenever the function returns from this point forward. // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we // want the pid to remain valid throughout that (and potentially future) usages. auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); }); if (PropertyChildReap(pid)) { return true; } // 利用FindServiceByPid函数,找到pid对应的服务 // FindServiceByPid主要经过轮询解析init.rc生成的service_list,找到pid与参数一致的srvc Service* svc = FindServiceByPid(pid); ... ... // 输出服务结束的缘由 if (!svc) { // 没有找到,说明已经结束了 return true; } svc->Reap(); // 根据svc的类型,决定后续的处理方式 if (svc->flags() & SVC_EXEC) { exec_waiter_.reset(); // 可执行服务则重置对应的waiter } if (svc->flags() & SVC_TEMPORARY) { RemoveService(*svc); // 移除临时服务 } return true; }
void Service::Reap() { // 清理未携带SVC_ONESHOT 或 携带了SVC_RESTART标志的srvc的进程组 if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) { KillProcessGroup(SIGKILL); } // Remove any descriptor resources we may have created. // 清除srvc中建立出的任意描述符 std::for_each(descriptors_.begin(), descriptors_.end(), std::bind(&DescriptorInfo::Clean, std::placeholders::_1)); // 清理工做完毕后,后面决定是否重启机器或重启服务 // TEMP服务不用参与这种判断 if (flags_ & SVC_TEMPORARY) { return; } pid_ = 0; flags_ &= (~SVC_RUNNING); // Oneshot processes go into the disabled state on exit, // except when manually restarted. // 对于携带了SVC_ONESHOT而且未携带SVC_RESTART的srvc,将这类服务的标志置为SVC_DISABLED,再也不自启动 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(); // 未携带SVC_RESTART的关键服务,在规定的间隔内,crash字数过多时,会致使整机重启; if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) { if (now < time_crashed_ + 4min) { if (++crash_count_ > 4) { LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes"; panic(); } } else { time_crashed_ = now; crash_count_ = 1; } } // 将待重启srvc的标志位置为SVC_RESTARTING(init进程将根据该标志位,重启服务) flags_ &= (~SVC_RESTART); flags_ |= SVC_RESTARTING; // Execute all onrestart commands for this service. // 重启在init.rc文件中带有onrestart选项的服务 onrestart_.ExecuteAllCommands(); NotifyStateChange("restarting"); return; }
不难看出,Reap函数的主要做用就是清除问题进程相关的资源,而后根据进程对应的类型,决定是否重启机器或重启进程。
咱们在这一部分的最后,看看定义于system/core/init/Action.cpp中的ExecuteAllCommands函数:
void Action::ExecuteAllCommands() const { for (const auto& c : commands_) { ExecuteCommand(c); } } void Action::ExecuteCommand(const Command& command) const { android::base::Timer t; // 进程重启时,将执行对应的函数 int result = command.InvokeFunc(); // 打印log auto duration = t.duration(); // Any action longer than 50ms will be warned to user as slow operation if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) { std::string trigger_name = BuildTriggersString(); std::string cmd_str = command.BuildCommandString(); LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_ << ":" << command.line() << ") returned " << result << " took " << duration.count() << "ms."; } }
整个signal_handler_init的内容比较多,在此总结一下:signal_handler_init的本质就是监听子进程死亡的信息,而后进行对应的清理工做,并根据死亡进程的类型,决定是否须要重启进程或机器。
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ /* 04. 建立epoll句柄 */ /* 05. 装载子进程信号处理器 */ property_load_boot_defaults(); // 进程调用property_load_boot_defaults进行默认属性配置相关的工做 export_oem_lock_status(); // 最终就是决定"ro.boot.flash.locked"的值 start_property_service(); // 启动属性服务 set_usb_controller(); ... ... }
老样子,这边咱们跟踪几个重要的函数。
void property_load_boot_defaults() { // 就是从各类路径读取默认配置 // load_properties_from_file的基本操做就是read_file,而后解析并设置 if (!load_properties_from_file("/system/etc/prop.default", NULL)) { // Try recovery path if (!load_properties_from_file("/prop.default", NULL)) { // Try legacy path load_properties_from_file("/default.prop", NULL); } } load_properties_from_file("/odm/default.prop", NULL); load_properties_from_file("/vendor/default.prop", NULL); update_sys_usb_config(); // 就是设置"persist.sys.usb.config"相关的配置 }
如代码所示,property_load_boot_defaults 实际上就是调用 load_properties_from_file 解析配置文件,而后根据解析的结果,设置系统属性。
void start_property_service() { property_set("ro.property_service.version", "2"); // 建立了一个非阻塞socket property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, 0666, 0, 0, nullptr, sehandle); if (property_set_fd == -1) { PLOG(ERROR) << "start_property_service socket creation failed"; exit(1); } // 调用listen函数监听property_set_fd, 因而该socket变成一个server listen(property_set_fd, 8); // 监听server socket上是否有数据到来 register_epoll_handler(property_set_fd, handle_property_set_fd); }
init进程在共享内存区域中,建立并初始化属性域。其它进程能够访问属性域中的值,但更改属性值仅能在init进程中进行。这就是init进程调用start_property_service的缘由。
其它进程修改属性值时,要预先向init进程提交值变动申请,而后init进程处理该申请,并修改属性值。在访问和修改属性时,init进程均可以进行权限控制。
/* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ /* 04. 建立epoll句柄 */ /* 05. 装载子进程信号处理器 */ /* 06. 启动属性服务*/ // system/core/init/builtins.cpp,定义Action中的function_map_为BuiltinFuntionMap const BuiltinFunctionMap function_map; // 在Action中保存function_map对象,记录了命令与函数之间的对应关系 Action::set_function_map(&function_map); /* 07. 匹配命令和函数之间对应关系 */ /* ------------ 第二阶段 ------------ END ------------ */ ... ... }
至此,init进程的准备工做执行完毕, 接下来就要开始解析init.rc文件了。
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ /* 04. 建立epoll句柄 */ /* 05. 装载子进程信号处理器 */ /* 06. 启动属性服务*/ /* 07. 匹配命令和函数之间对应关系 */ /* ------------ 第二阶段 ------------ END ------------ */ /* ------------ 第三阶段 ----------- BEGIN------------ */ ActionManager& am = ActionManager::GetInstance(); ServiceManager& sm = ServiceManager::GetInstance(); Parser& parser = Parser::GetInstance(); // 构造解析文件用的parser对象 // 为一些类型的关键字,建立特定的parser // 增长ServiceParser为一个section,对应name为service parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); // 增长ActionParser为一个section,对应name为action parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); // 增长ActionParser为一个section,对应name为import parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); std::string bootscript = GetProperty("ro.boot.init_rc", ""); // 判断是否存在bootscript // 若是没有bootscript,则解析init.rc文件 if (bootscript.empty()) { // 8.0引入 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 { // 若存在bootscript, 则解析bootscript 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); } ... ... }
若是没有定义bootScript,那么init进程仍是会解析init.rc文件。init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操做。
此处解析函数传入的参数为“/init.rc”,解析的是运行时与init进程同在根目录下的init.rc文件。该文件在编译前,定义于system/core/rootdir/init.rc中。
✨ 继续往下分析main函数以前;
✨ 咱们先了解一下init.rc是什么,而后分析下parser解析init.rc过程;
✨ 最后咱们再继续跟源码!
init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,主要包含五种类型语句:<font color=#87CEFA>Action、Command、Service、Option 和 Import</font>,在分析代码的过程当中咱们会详细介绍。
init.rc的配置代码在:system/core/rootdir/init.rc中。
init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操做。
init.rc文件大体分为两大部分:
一部分是以“on”关键字开头的动做列表(action list):
on early-init // Action类型语句 # Set init and its forked children's oom_adj. // #:注释符号 write /proc/1/oom_score_adj -1000 ... ... # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628. mkdir /mnt 0775 root system ... ... start ueventd
Action类型语句格式:
on <trigger> [&& <trigger>]* // 设置触发器 <command> <command> // 动做触发以后要执行的命令
另外一部分是以“service”关键字开头的服务列表(service list): 如 Zygote
service ueventd /sbin/ueventd // Service类型语句 class core critical seclabel u:r:ueventd:s0
Service类型语句格式:
service <name> <pathname> [ <argument> ]* // <service的名字><执行程序路径><传递参数> <option> // option是service的修饰词,影响何时、如何启动services <option> ...
借助系统环境变量或Linux命令,
🏹 动做列表用于建立所需目录,以及为某些特定文件指定权限;
🏹 服务列表用来记录init进程须要启动的一些子进程,如上面代码所示,service关键字后的第一个字符串表示服务(子进程)的名称,第二个字符串表示服务的执行路径。
值得一提的是从Android 7.0后的源码,对init.rc文件进行了拆分,每一个服务一个rc文件。咱们要分析的zygote服务的启动脚本则在init.zygoteXX.rc中定义。
在init.rc的import段咱们看到以下代码:
import /init.${ro.zygote}.rc // 能够看出init.rc再也不直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不一样的文件
从android5.0开始,android开始支持64位的编译,zygote自己也就有了32位和64位的区别,因此在这里用ro.zygote属性来控制启动不一样版本的zygote进程。
init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。分别是init.zygote32.rc,init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc,由硬件决定调用哪一个文件。
这里拿32位处理器为例,init.zygote32.rc的代码以下所示:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main # class是一个option,指定zygote服务的类型为main priority -20 user root group root readproc # socket关键字表示一个option,建立一个名为dev/socket/zygote,类型为stream,权限为660的socket socket zygote stream 660 root system # onrestart是一个option,说明在zygote重启时须要执行的command onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond writepid /dev/cpuset/foreground/tasks
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server语句解读:
在Init.zygote32.rc中,定义了一个zygote服务:zygote,由关键字service告诉init进程建立一个名为zygote的进程,这个进程要执行的程序是:/system/bin/app_process,给这个进程四个参数:
🏹 -Xzygote:该参数将做为虚拟机启动时所需的参数
🏹 /system/bin:表明虚拟机程序所在目录
🏹 --zygote:指明以ZygoteInit.java类中的main函数做为虚拟机执行入口
🏹 --start-system-server:告诉Zygote进程启动systemServer进程
回顾解析init.rc的代码:
int main(int argc, char** argv) { ... ... ActionManager& am = ActionManager::GetInstance(); ServiceManager& sm = ServiceManager::GetInstance(); Parser& parser = Parser::GetInstance(); // 构造解析文件用的parser对象 // 增长ServiceParser为一个section,对应name为service parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); // 增长ActionParser为一个section,对应name为action parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); // 增长ActionParser为一个section,对应name为import parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { 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); } ... ... }
咱们发如今解析前,使用了Parser类(在init目录下的 <font color=#00FFFF>init_parser.h</font> 中定义),构造了parser对象:
Parser& parser = Parser::GetInstance(); // 构造解析文件用的parser对象
初始化ServiceParser用来解析 “service”块,ActionParser用来解析"on"块,ImportParser用来解析“import”块,“import”是用来引入一个init配置文件,来扩展当前配置的。
// 增长ServiceParser为一个section,对应name为service parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); // 增长ActionParser为一个section,对应name为action parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); // 增长ActionParser为一个section,对应name为import parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
/system/core/init/readme.txt中对init文件中的全部关键字作了介绍,主要包含了Actions, Commands, Services, Options, and Imports等,可自行学习解读。
下面就是分析解析过程了:parser.ParseConfig("/init.rc") (函数定义于system/core/init/init_parser.cpp中)
bool Parser::ParseConfig(const std::string& path) { if (is_dir(path.c_str())) { // 判断传入参数是否为目录地址 return ParseConfigDir(path); // 递归目录,最终仍是靠ParseConfigFile来解析实际的文件 } return ParseConfigFile(path); // 传入参数为文件地址 }
先来看看ParseConfigDir函数:
bool Parser::ParseConfigDir(const std::string& path) { LOG(INFO) << "Parsing directory " << path << "..."; std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir); if (!config_dir) { PLOG(ERROR) << "Could not import directory '" << path << "'"; return false; } // 递归目录,获得须要处理的文件 dirent* current_file; std::vector<std::string> files; while ((current_file = readdir(config_dir.get()))) { // Ignore directories and only process regular files. if (current_file->d_type == DT_REG) { std::string current_path = android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name); files.emplace_back(current_path); } } // Sort first so we load files in a consistent order (bug 31996208) std::sort(files.begin(), files.end()); for (const auto& file : files) { // 容易看出,最终还是调用ParseConfigFile if (!ParseConfigFile(file)) { LOG(ERROR) << "could not import file '" << file << "'"; } } return true; }
接下来就重点分析ParseConfigFile():
bool Parser::ParseConfigFile(const std::string& path) { ... ... android::base::Timer t; std::string data; std::string err; if (!ReadFile(path, &data, &err)) { // 读取路径指定文件中的内容,保存为字符串形式 LOG(ERROR) << err; return false; } ... ... ParseData(path, data); // 解析获取的字符串 ... ... return true; }
ParseConfigFile只是读取文件的内容并转换为字符串,实际的解析工做被交付给ParseData。
ParseData函数定义于system/core/init/init_parser.cpp中,负责根据关键字解析出服务和动做。动做与服务会以链表节点的形式注册到service_list与action_list中,service_list与action_list是init进程中声明的全局结构体。
跟踪ParseData():
void Parser::ParseData(const std::string& filename, const std::string& data) { //TODO: Use a parser with const input and remove this copy // copy一波数据 std::vector<char> data_copy(data.begin(), data.end()); data_copy.push_back('\0'); // 解析用的结构体 parse_state state; state.line = 0; state.ptr = &data_copy[0]; state.nexttoken = 0; SectionParser* section_parser = nullptr; std::vector<std::string> args; for (;;) { // next_token以行为单位分割参数传递过来的字符串,初始没有分割符时,最早走到T_TEXT分支 switch (next_token(&state)) { case T_EOF: if (section_parser) { section_parser->EndSection(); // 解析结束 } return; case T_NEWLINE: state.line++; if (args.empty()) { break; } ... ... // 在前文建立parser时,咱们为service,on,import定义了对应的parser // 这里就是根据第一个参数,判断是否有对应的parser if (section_parsers_.count(args[0])) { if (section_parser) { // 结束上一个parser的工做,将构造出的对象加入到对应的service_list与action_list中 section_parser->EndSection(); } // 获取参数对应的parser section_parser = section_parsers_[args[0]].get(); std::string ret_err; // 调用实际parser的ParseSection函数 if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) { LOG(ERROR) << filename << ": " << state.line << ": " << ret_err; section_parser = nullptr; } } else if (section_parser) { std::string ret_err; /* * 若是第一个参数不是service,on,import * 则调用前一个parser的ParseLineSection函数 * 这里至关于解析一个参数块的子项 */ if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) { LOG(ERROR) << filename << ": " << state.line << ": " << ret_err; } } // 清空本次解析的数据 args.clear(); break; case T_TEXT: // 将本次解析的内容写入到args中 args.emplace_back(state.text); break; } } }
上面的代码看起来比较复杂,但实际上就是面向对象,根据不一样的关键字,使用不一样的parser对象进行解析。
至此,init.rc解析完!Ok,别忘了,main函数尚未分析完,继续往下看。
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ /* 04. 建立epoll句柄 */ /* 05. 装载子进程信号处理器 */ /* 06. 启动属性服务*/ /* 07. 匹配命令和函数之间对应关系 */ /* ------------ 第二阶段 ------------ END ------------ */ /* ------------ 第三阶段 ----------- BEGIN------------ */ /* init解析 */ /* ------------ 第三阶段 ----------- END ------------ */ /* ------------ 第四阶段 ----------- BEGIN------------ */ // 经过am对命令执行顺序进行控制 // ActionManager& am = ActionManager::GetInstance(); // init执行命令触发器主要分为early-init,init,late-init,boot等 am.QueueEventTrigger("early-init"); // 添加触发器early-init,执行on 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(set_kptr_restrict_action, "set_kptr_restrict"); am.QueueBuiltinAction(keychord_init_action, "keychord_init"); am.QueueBuiltinAction(console_init_action, "console_init"); // Trigger all the boot actions to get us started. // 添加触发器init,执行on init内容,主要包括建立/挂在一些目录,以及symlink等 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 = GetProperty("ro.bootmode", ""); if (bootmode == "charger") { am.QueueEventTrigger("charger"); // on charger阶段 } else { am.QueueEventTrigger("late-init"); // 非充电模式添加触发器last-init } // Run all property triggers based on current state of the properties. am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers"); }
继续分析main函数:
int main(int argc, char** argv) { /* ------------ 第一阶段 ------------ BEGIN------------ */ /* 01. 判断及增长环境变量 */ /* 02. 建立文件系统目录并挂载相关的文件系统 */ /* 03. 重定向输入输出/内核Log系统 */ /* 04. 挂在一些分区设备 */ /* 05. 完成SELinux相关工做 */ /* 06. is_first_stage 收尾 */ /* ------------ 第一阶段 ------------- END ------------ */ /* ------------ 第二阶段 ------------ BEGIN------------ */ /* 01. 初始化属性域 */ /* 02. 清空环境变量 */ /* 03. 完成SELinux相关工做 */ /* 04. 建立epoll句柄 */ /* 05. 装载子进程信号处理器 */ /* 06. 启动属性服务*/ /* 07. 匹配命令和函数之间对应关系 */ /* ------------ 第二阶段 ------------ END ------------ */ /* ------------ 第三阶段 ----------- BEGIN------------ */ /* init解析 */ /* ------------ 第三阶段 ----------- END ------------ */ /* ------------ 第四阶段 ----------- BEGIN------------ */ /* 01. 向执行队列中添加其余action */ /* 02. 其他工做 */ while (true) { // 判断是否有事件须要处理 // By default, sleep until something happens. int epoll_timeout_ms = -1; if (do_shutdown && !shutting_down) { do_shutdown = false; if (HandlePowerctlMessage(shutdown_command)) { shutting_down = true; } } if (!(waiting_for_prop || sm.IsWaitingForExec())) { am.ExecuteOneCommand(); // 依次执行每一个action中携带command对应的执行函数 } if (!(waiting_for_prop || sm.IsWaitingForExec())) { if (!shutting_down) restart_processes(); // 重启一些挂掉的进程 // 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; // 有action待处理,不等待 } epoll_event ev; // 没有事件到来的话,最多阻塞timeout时间 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) { //有事件到来,执行对应处理函数 //根据上文知道,epoll句柄(即epoll_fd)主要监听子进程结束,及其它进程设置系统属性的请求 ((void (*)()) ev.data.ptr)(); } } } // end main
至此,Init.cpp的main函数分析完毕!init进程已经启动完成,一些重要的服务如core服务和main服务也都启动起来,并启动了zygote(/system/bin/app_process64)进程,zygote初始化时会建立虚拟机,启动systemserver等。