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

  • 相关阅读:
    6-5 函数
    6-2 触发器
    4、MongoDB学习之备份还原
    3、MongoDB学习之固定集合
    2、MongoDB学习之索引的管理
    1、MongoDB学习之基本操作
    JS-01
    | 和 ||,& 和 && 的区别
    正则
    设计测试用例的经验总结
  • 原文地址:https://www.cnblogs.com/hzl6255/p/12026599.html
Copyright © 2020-2023  润新知