Android init介绍(上)
1. 介绍
init进程是Linux系统第一个用户进程,是Android系统应用程序的根进程,即1号进程(PID为1);Android中的init文件位于/init,代码位于system/core/init目录
Linux中第一个进程为init_task,也即0号进程(PID为0),init进程由init_task进程fork而来,在kernel初始化完成后init_task便化身为idle进程
更多内核初始化init_task和init进程的信息,参考<Android 8.0 : 系统启动流程之Linux内核>
首先说明一下,笔者的代码分析基于Android 9.0
------------------------------------------------------------------------------- | 镜像 | 内容 | 挂载点 | 加载方式 | ------------------------------------------------------------------------------- | ramdisk.img | $(OUT)/root | / | 内核加载 | | ramdisk-recovery.img | $(OUT)/recovery/root | / | | | boot.img | $(OUT)/kernel + ramdisk.img | | | | recovery.img | ramdisk-recovery.img | | | | system.img | $(OUT)/system | /system | init加载 | | userdata.img | $(OUT)/data | /data | init加载 | -------------------------------------------------------------------------------
2. 云竹
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(); }
2.1 ueventd和watchdogd
在Android.mk中
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
这里是在/sbin/目录下创建init的软连接,因为ueventd和watchdogd应用的代码也位于init目录中,通过程序名称来决定运行的代码
而在system/core/rootdir/init.rc文件中
on early-init ... start ueventd ... service ueventd /sbin/ueventd class core critical seclabel u:r:ueventd:s0 shutdown critical
由此可见,ueventd会在init解析rc文件的early-init阶段被执行;而watchdogd由厂商来定制是否要运行
2.2 信号处理
当编译userdebug或者eng版本时,会在Android.mk中打开REBOOT_BOOTLOADER_ON_PANIC选项,该选项打开时会注册init进程的特殊信号处理函数,当init收到SIGABRT、SIGBUS、SIGSEGV等异常信号时Android系统将进入bootloader模式
3. 第一阶段
这里简单介绍下Android Device Tree(DT)
// CASE 1: 默认路径 /proc/device-tree/firmware/android // CASE 2: 内核命令行/proc/cmdline中定义 // androidboot.android_dt_dir=/sys/bus/platform/devices/ANDR0001:00/properties/android/ /sys/bus/platform/devices/ANDR0001:00/properties/android/
DT中定义了Android中初始化中需要的一些参数, 笔者该目录内容如下
# tree /sys/bus/platform/devices/ANDR0001:00/properties/android/ . |---compatible // "android,firmware" |---fstab | |---compatible // "android,fstab" | |---product | | |---compatible // "android,product" | | |---dev // "/dev/block/pci/pci0000:00/0000:00:1c.0/by-name/product" | | |---fsmgr_flags // "wait,slotselect,avb" | | |---mnt_flags // "ro" | | |---type // "ext4" | |---vendor | | |---compatible // "android,vendor" | | |---dev // "/dev/block/pci/pci0000:00/0000:00:1c.0/by-name/vendor" | | |---fsmgr_flags // "wait,slotselect,avb" | | |---mnt_flags // "ro" | | |---type // "ext4" |---vbmeta | |---compatible // "android,vbmeta" | |---parts // "vbmeta,boot,system,vendor,tos,product"
3.1 挂载文件系统
首先挂载了如下文件系统
---------------------------------------------- | 设备 | 类型 | 挂载目录 | ---------------------------------------------- | tmpfs | tmpfs | /dev | | devpts | devpts | /dev/pts | | proc | proc | /proc | | sysfs | sysfs | /sys | | selinuxfs | selinuxfs | /sys/fs/selinux | | tmpfs | tmpfs | /mnt | ----------------------------------------------
创建如下文件夹
/dev/socket: 用于Android套接字
创建如下字符设备文件
/dev/kmsg: 1/11 /dev/kmsg_debug: 1/11 /dev/random: 1/8 /dev/urandom: 1/9
3.2 日志初始化
InitKernelLogging(argv) // FIXME: 将[标准输入/标准输出/错误输出]重定向到/dev/null open("/sys/fs/selinux/null", O_RDWR); dup2(fd, 0/1/2); // 设置日志输出函数, 然后获取环境变量ANDROID_LOG_TAGS并解析从而设置最小输出等级 android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter) // 将日志写入/dev/kmsg android::base::KernelLogger
3.3 挂载分区
DoFirstStageMount() // 检测DT的fstab配置是否支持, 如果支持则直接跳过挂载 android::init::is_android_dt_value_expected("fstab/compatible", "android,fstab") // 获取Android DT(设备树)并获取$(DT)/fstab/compatible的值, 然后与android,fstab比较 android::init::read_android_dt_file("fstab/compatible", &dt_content) // 检测DT的vbmeta配置, 如果支持则使用FirstStageMountVBootV2, 否则使用FirstStageMountVBootV1 android::init::FirstStageMount::Create() android::init::FirstStageMount::FirstStageMount() // 获取$(DT)/fstab/目录定义的分区及属性, 笔者当前包含product和vendor fs_mgr_read_fstab_dt() read_fstab_from_dt() fs_mgr_read_fstab_file() /* * 读取fstab * - 首先尝试$(DT)/boot_devices * - 不成功则依次尝试 * /odm/etc/fstab.$(platform) * /vendor/etc/fstab.$(platform) * /fstab.$(platform) */ fs_mgr_get_boot_devices() // 获取$(DT)/vbmeta/parts的值, 当前为: vbmeta,boot,system,vendor,tos,product android::init::FirstStageMountVBootV2::FirstStageMountVBootV2() // 挂载fastb中的分区列表 android::init::FirstStageMount::DoFirstStageMount() android::init::FirstStageMount::InitDevices() android::init::FirstStageMount::GetRequiredDevices() android::init::FirstStageMount::InitRequiredDevices() // 依次挂载各分区 android::init::FirstStageMount::MountPartitions() android::init::FirstStageMount::SetUpDmVerity() fs_mgr_do_mount_one()
3.4 AVB初始化
SetInitAvbVersionInRecovery() // 如果不是Revery模式则直接返回 IsRecoveryMode() // 如果不兼容vbmeta则直接返回 IsDtVbmetaCompatible()
android::init::FirstStageMountVBootV2()
// 获取vbmeta/parts对应的分区
read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)
// 初始化设备
android::init::FirstStageMount::InitDevices() // 获取AVB Handler FsManagerAvbHandle::Open(FirstStageMountVBootV2::by_name_symlink_map_) // 设置环境变量INIT_AVB_VERSION setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1)
AVB(Android Verified Boot),主要用于防止系统文件本身被篡改,还包含了防止系统回滚的功能
3.5 seccomp初始化
// 此处决定是否使能全局seccomp; 如果没有使能, zygote也会使能 // 读取/proc/cmdline内容, 如果包含androidboot.seccomp=global则调用set_global_seccomp_filter // set_global_seccomp_filter位于bionic/libc/seccomp/seccomp_policy.cpp global_seccomp()
3.6 SELinux初始化
// 将selinux日志重定向到内核日志输出, 即/dev/kmsg SelinuxSetupKernelLogging() SelinuxInitialize() /* * 加载SELinux策略
* 如果IsSplitPolicyDevice返回true, 执行LoadSplitPolicy, 否则执行LoadMonolithicPolicy
*/ android::init::LoadPolicy() // 检测/system/etc/selinux/plat_sepolicy.cil文件是否存在 android::init::IsSplitPolicyDevice() android::init::LoadSplitPolicy() /* * 依次查找以下预编译SELinux文件
* "/vendor/etc/selinux/precompiled_sepolicy" * "/odm/etc/selinux/precompiled_sepolicy" */ android::init::FindPrecompiledSplitPolicy(&file) open(file, O_RDONLY | O_CLOEXEC | O_BINARY) selinux_android_load_policy_from_fd(fd, file) set_selinuxmnt("/sys/fs/selinux") mmap(NULL, , PROT_READ, MAP_PRIVATE, fd, 0) // 将策略文件写入到 /sys/fs/selinux/load security_load_policy(,) // 从文件/sepolicy加载策略
android::init::LoadMonolithicPolicy() selinux_android_load_policy() open("/sepolicy", O_RDONLY | O_NOFOLLOW | O_CLOEXEC) selinux_android_load_policy_from_fd(fd, "/sepolicy") /* * 设置selinux状态
* 如果内核selinux状态与自定义selinux状态进行比较, 最终以自定义selinux状态为准
*/ // 获取内核selinux状态 security_getenforce() // 获取Android自定义selinux状态 android::init::IsEnforcing() // 如果未定义ALLOW_PERMISSIVE_SELINUX, 直接返回Enforcing, 否则才调用StatusFromCmdline // 读取/proc/cmdline内容, 如果包含androidboot.selinux=permissive则返回Permissive android::init::StatusFromCmdline() // 设置当前selinux状态, 其实是将0/1写入/sys/fs/selinux/enforce security_setenforce() // 设置checkreqprot为0, 该值决定SELinux通过程序(1)还是通过内核(0)响应进行安全检查
WriteFile("/sys/fs/selinux/checkreqprot", "0") // 获取SELinux策略加载时间并设置到设置环境变量INIT_SELINUX_TOOK setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1); // 将init文件的Context恢复成file_contexts中的初始配置
selinux_android_restorecon("/init", 0)
SEAndroid整体架构图如下图所示
3.7 第二阶段准备
// 将环境变量INIT_SECOND_STAGE设置为1, 准备进入第二阶段 setenv("INIT_SECOND_STAGE", "true", 1); // 设置环境变量INIT_STARTED_AT setenv("INIT_STARTED_AT", start_ms, 1); // 重新执行/init execv("/init", { /init, nullptr });
4. 第二阶段
4.1 创建密钥环
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
关于密钥环,参考:
<Linux密钥保留服务入门>
<Kernel Key Retention Service>
4.2 启动准备
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
创建/dev/.booting文件,用于标识当前正在启动中,当firmware_mounts_complete被触发时删除
4.3 Property服务初始化
property_init() // 创建目录: O/RWX、G/X、O/X mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH) // 创建序列化的System Property CreateSerializedPropertyInfo() // 读取System Property并加载到PropertyInfoEntry LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts", &e) LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts", &e) LoadPropertyInfoFromFile(/vendor/etc/selinux/nonplat_property_contexts", &e) // 将加载的System Property序列化 BuildTrie(e, "u:object_r:default_prop:s0", "string", &s) // 将序列化的System Property写入文件 WriteStringToFile(s, "/dev/__properties__/property_info",,,,) // 恢复property_info文件的Context selinux_android_restorecon("/dev/__properties__/property_info", 0) // 初始化System Property内存区域 __system_property_area_init() SystemProperties::AreaInit("/dev/__properties__", false) ContextsSerialized::Initialize(true, "/dev/__properties__", false) ContextsSerialized::InitializeProperties() // 加载System Property默认路径 android::properties::PropertyInfoAreaFile::LoadDefaultPath() // 将文件加载到内存 LoadPath("/dev/__properties__/property_info") // 初始化ContextNode ContextsSerialized::InitializeContextNodes() // 获取ContextNode数量 android::properties::PropertyInfoAreaFile::num_contexts() // 创建num_contexts * ContextNode大小共享内核 mmap(, , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS , -1, 0) // 创建ContextNode ContextNode(PropertyInfoAreaFile::context(i), "/dev/__properties__") // 将所有property对应的Security Context写入文件
ContextNode::Open(true, false)
// 将property序列化并保持至/dev/__properties/properties_serial ContextsSerialized::MapSerialPropertyArea(true, false)
以下是初始化完成后/dev/__properties__目录文件列表
# ls -al /dev/__properties__ drwx--x--x 2 root root 2360 2011-11-11 19:11 . drwxr-xr-x 18 root root 4820 2011-11-11 19:11 .. -r--r--r-- 1 root root 131072 2011-11-11 19:11 properties_serial -r--r--r-- 1 root root 29660 2011-11-11 19:11 property_info -r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:audio_prop:s0 -r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:bluetooth_prop:s0 ... -r--r--r-- 1 root root 131072 2019-10-01 08:00 u:object_r:system_prop:s0 -r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:system_radio_prop:s0 ... -r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:vendor_default_prop:s0 -r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:vendor_persist_prop:s0 ... -r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:wifi_prop:s0
4.4 处理设备树和命令行
/* * DT中的属性优先级高于内核命令行的属性
*/ // 处理Android设备树中定义的属性 process_kernel_dt(); // 检测是否兼容Android, 否则直接返回 is_android_dt_value_expected("compatible", "android,firmware") // 读取/proc/cmdline, 将androidboot.name=value的键值对保存为ro.boot.name=value属性 process_kernel_cmdline(); import_kernel_cmdline(false, ) import_kernel_nv(key, value) property_set("ro.boot." + key.substr(12), value)
4.5 RO属性相关
/* * 将部分ro.boot.*属性转换为ro.*属性, 前者值不存在则设置默认值
* { "ro.boot.serialno", "ro.serialno", "", }, * { "ro.boot.mode", "ro.bootmode", "unknown", }, * { "ro.boot.baseband", "ro.baseband", "unknown", }, * { "ro.boot.bootloader", "ro.bootloader", "unknown", }, * { "ro.boot.hardware", "ro.hardware", "unknown", }, * { "ro.boot.revision", "ro.revision", "0", }, */ export_kernel_boot_props() // 将第一阶段设置的环境变量设置为property属性
property_set("ro.boottime.init", getenv("INIT_STARTED_AT")); property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK")); property_set("ro.boot.avb_version", getenv("INIT_AVB_VERSION")) // 清空环境变量
unsetenv("INIT_SECOND_STAGE"); unsetenv("INIT_STARTED_AT"); unsetenv("INIT_SELINUX_TOOK"); unsetenv("INIT_AVB_VERSION");
4.6 SELinux再次初始化
// 将selinux日志重定向到内核日志输出, 即/dev/kmsg SelinuxSetupKernelLogging(); // FIXME: SelabelInitialize(); selinux_android_file_context_handle() selinux_android_set_sehandle() // 恢复file_contexts中的初始配置 SelinuxRestoreContext(); selinux_android_restorecon("/dev", 0); selinux_android_restorecon("/dev/kmsg", 0); ...
4.7 EPOLL实例
// 创建EPOLL epoll_create1(EPOLL_CLOEXEC) /* * 创建一个相互连接的套接字对 * 接收SIGCHLD信号时往其中一个套接字写 * 另一个套接字的读则注册到EPOLL中 */ sigchld_handler_init() socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) act.sa_handler = SIGCHLD_handler; |-> write(signal_write_fd, "1", 1) sigaction(SIGCHLD, &act, 0); register_epoll_handler(signal_read_fd, handle_signal); read(signal_read_fd, buf, sizeof(buf)) <-| ReapAnyOutstandingChildren() <-| /* * 添加SIGTERM信号处理函数, 并注册到EPOLL中 */ InstallSigtermHandler() sigaddset(&mask, SIGTERM); sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC); register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal); read(sigterm_signal_fd, &, ) <-| HandlePowerctlMessage("shutdown,container") <-|
EPOLL的使用方法如下:
- epoll_create/epoll_create1创建一个EPOLL实例
- epoll_ctl向EPOLL实例注册/修改/删除监听事件 - epoll_wait等待事件发生 - close关闭EPOLL实例
4.8 Property相关服务
// 加载系统启动属性 property_load_boot_defaults(); load_properties_from_file("/system/etc/prop.default", NULL) load_properties_from_file("/product/build.prop", NULL) load_properties_from_file("/odm/default.prop", NULL) load_properties_from_file("/vendor/default.prop", NULL) ReadFile() LoadProperties() // 将property键值对设置到系统中 HandlePropertySet(key, value, ,) // 设置persist.sys.usb.config属性值 update_sys_usb_config() /* * OEM Lock和AVB相关, 涉及如下属性 * ro.oem_unlock_supported * ro.boot.verifiedbootstate * ro.boot.flash.locked */ export_oem_lock_status(); // 启动属性系统服务 start_property_service(); // 创建socket, 用于监听写系统属性的请求 CreateSocket("property_service") lisent(fd, 8) // 注册到EPOLL中 register_epoll_handler(fd, handle_property_set_fd) HandlePropertySet(key, value)<-| // 设置sys.usb.controller属性值 set_usb_controller();
下一篇请参考<Android init介绍(下)>