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整体架构图如下图所示

SEAndroid_structure

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介绍(下)>

posted @ 2019-12-11 23:52  北落不吉  阅读(3558)  评论(0编辑  收藏  举报