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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM