Android 系统启动之 Init 进程启动分析三

本文基于 AOSP android-10.0.0_r41 版本讲解,内核版本 android-goldfish-4.14-gchips

上一节我们分析了 init 进程的 selinux 初始化过程,最后会进入 second_stage 阶段:

cpp 复制代码
int SecondStageMain(int argc, char** argv) {

    // 注册信号处理函数,出现一些严重错误时重启
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    //标准输入输出重定向到 /dev/null 
    SetStdioToDevNull(argv); 
    // Init 阶段的 log 输出到 /dev/msg LOG PLOG
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

这部分和第一阶段类似:

  • 注册信号处理函数,出现一些严重错误时重启系统
  • 将标准输入输出重定向到 /dev/null
  • 将 Init 阶段的 log (通过 LOG PLOG 宏)输出到 /dev/msg
cpp 复制代码
    // oom_adj 表示进程的优先级,值越小,优先级越大
    // 设置进程的优先级
    // Set init and its forked children's oom_adj.
    if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
        LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
    }

这里设置 init 进程的 oom_adj 值,oom_adj 表示进程的优先级,值越小,优先级越大。

cpp 复制代码
    // Secommp (SECure COMPuting) 是 Linux 内核 2.6.12 版本引入的安全模块,主要是用来限制某一进程可用的系统调用 (system call),这里开启这个机制,了解即可
    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
    GlobalSeccomp();

Secommp (SECure COMPuting) 是 Linux 内核 2.6.12 版本引入的安全模块,主要是用来限制某一进程可用的系统调用 (system call),这里开启这个机制,了解即可

cpp 复制代码
    // 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.

    // 这里使用到的是内核提供给用户空间使用的 密钥保留服务 (key retention service),它的主要意图是在 Linux 内核中缓存身份验证数据。远程文件系统和其他内核服务可以使用这个服务来管理密码学、身份验证标记、跨域用户映射和其他安全问题。它还使 Linux 内核能够快速访问所需的密钥,并可以用来将密钥操作(比如添加、更新和删除)委托给用户空间。

    // 了解即可
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

这里使用到的是内核提供给用户空间使用的密钥保留服务 (key retention service),它的主要意图是在 Linux 内核中缓存身份验证数据。远程文件系统和其他内核服务可以使用这个服务来管理密码学、身份验证标记、跨域用户映射和其他安全问题。它还使 Linux 内核能够快速访问所需的密钥,并可以用来将密钥操作(比如添加、更新和删除)委托给用户空间。了解即可

cpp 复制代码
    //创建 /dev/.booting 文件,就是个标记,表示booting进行中
    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

创建 /dev/.booting 文件,就是个标记,表示 booting 进行中

cpp 复制代码
    // 属性系统初始化,基础组件篇已经讲过了
    property_init();

属性系统初始化,基础组件篇已经讲过了

cpp 复制代码
    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.

     //uboot启动时可以给出启动参数: androidboot.android_dt_dir 指定一个路径
    // 没有指定就是默认路径: /proc/device-tree/firmware/android/
    //一般这个路径中会设置一个 fstab, 用于指定 vendor 和其他分区的挂载点


    // 读取特定设备树信息,并设置 ro.boot. 开头的属性。
    process_kernel_dt();
    // 根据内核启动参数设置启动相关的属性:ro.boot 开头的属性

    // 将内核中 cmdline 中所有的有 = 号的参数,以及特殊的 androidboot.xx=xxx 的形式设置成相应的属性
    // 如果是模拟器, bootargs 中包含: androidboot.hardware=ranchu 和 init=/init
    // 就会设置ro.kernel.androidboot.hardware= ranchu  ro.kernel.init=/init 和ro.boot.hardware=ranchu, 
    // 如果是真机, 只针对 androidboot.xx=xx 的参数设置, 
    // 如 androidboot.storagemedia=emmc, 就会有 ro.boot.storagemedia=emmc
    process_kernel_cmdline();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.

    // 将列表中特定属性的值设置到另外一个属性的值中去
    // 如将 ro.boot.serialno 的值设置到 ro.serialno 中去
    //一般 ro.boot 开头的属性很多都是来自内核的 cmdline
    export_kernel_boot_props();

    //设置 init 和 selinux 执行时间
    // 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"));

    //设置avb版本
    // 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);

    // See if need to load debug props to allow adb root, when the device is unlocked.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }

    // Clean up our environment.
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");
    unsetenv("INIT_FORCE_DEBUGGABLE");

这里主要是设置一些属性和环境变量,了解一下

cpp 复制代码
    // Now set up SELinux for second stage.
    // 第二阶段再次设置 Selinux 日志
    SelinuxSetupKernelLogging();
    // 第二阶段再次设置安全上下文
    SelabelInitialize();
    //通过restorecon设置各个文件的默认安全上下文
    SelinuxRestoreContext();

Selinx 相关的配置,和第一阶段类似。

cpp 复制代码
    // 初始化epoll
    Epoll epoll;
    if (auto result = epoll.Open(); !result) {
        PLOG(FATAL) << result.error();
    }

    // 通过epoll监控子进程退出时发送SIGCHLD信号,并通过HandleSignalFd()进行处理
    InstallSignalFdHandler(&epoll);

初始化一个 epoll,然后通过 epoll 监控子进程退出时发送 SIGCHLD 信号,并通过 HandleSignalFd() 进行处理

cpp 复制代码
    // 加载各个分区中的属性文件,如 prop.default, build.pro, default.prop,之前详细讲过属性文件
    property_load_boot_defaults(load_debug_prop);

    //卸载挂载点debug_ramdisk
    UmountDebugRamdisk();
    // 主要是根据 ro.vndk.version 版本号,将/system/vendor_overlay/ 和 /product/vendor_overlay/ 挂载在 vendor 上
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();

    // 开启属性服务
    StartPropertyService(&epoll);
    MountHandler mount_handler(&epoll);

    // 读取USB设备控制器的节点/sys/class/udc/xxx,如fe800000.dwc3,并设置属性sys.usb.controller=fe800000.dwc3
    set_usb_controller();

    // ......
}

这里最核心的内容是调用 StartPropertyService 开启属性服务.

参考资料

相关推荐
dal118网工任子仪11 小时前
91,【7】 攻防世界 web fileclude
android·前端
taopi202411 小时前
android java 用系统弹窗的方式实现模拟点击动画特效
android
fanged11 小时前
Android学习19 -- 手搓App
android
dal118网工任子仪11 小时前
99,[7] buuctf web [羊城杯2020]easyphp
android·前端·android studio
村口老王16 小时前
鸿蒙开发——应用程序包及模块化设计
android·前端·harmonyos
6v6博客16 小时前
如何在 Typecho 中实现 Joe 编辑器标签自动填充
android·编辑器
程序员牛肉20 小时前
为什么网络上一些表情包在反复传播之后会变绿?“电子包浆”到底是怎么形成的?
android
志尊宝20 小时前
Android 深入探究 JSONObject 与 JSONArray:Android 中的数据解析与数组操作全解析
android
莫名有雪1 天前
BUUCTF_[网鼎杯 2020 朱雀组]phpweb(反序列化绕过命令)
android
爱写代码的山山1 天前
虚幻UE5手机安卓Android Studio开发设置2025
android·ue5·虚幻