在前一篇博客Android O: init进程启动流程分析(阶段一)中,
我们分析了init进程第一阶段(内核态)的流程。
在本篇博客中,我们来看看init进程第二阶段(用户态)的工作。
一、初始化属性域
init进程的第二阶段仍然从main函数开始入手。
int main(int argc, char** argv) {
//同样进行一些判断及环境变量设置的工作
..........
//现在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.
// 同样屏蔽标准输入输出及定义Kernel logger
InitKernelLogging(argv);
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.
// 最后调用syscall,设置安全相关的值
keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
// 这里的功能类似于“锁”
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
//初始化属性域
property_init();
//初始化完属性域后,以下均完成一些属性的设定
// 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中,
如下面代码所示,最终调用_system_property_area_init函数初始化属性域。
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
exit(1);
}
}
二、清空环境变量,完成selinux相关的工作
我们回到main函数,看看接下来的工作:
.......
// Clean up our environment.
// 清除掉之前使用过的环境变量
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
// 再次完成selinux相关的工作
selinux_initialize(false);
selinux_restore_context();
..............
在init进程的第一阶段,也调用selinux_initialize函数,
主要加载selinux相关的策略。
第二阶段调用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
// 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...";
restorecon("/dev");
restorecon("/dev/kmsg