Android开机流程-从Init进程启动到进入Android桌面

1. init进程启动流程

Android bootloader负责加载boot.img,将其内容放入内存,然后启动内核。内核接管之后,会解压并加载ramdisk到内存中,然后启动用户空间的第一个进程init

在Android系统启动过程中,ramdisk.img被内核直接解压到内存中并用作初始根文件系统。这一过程不是通过挂载块设备来实现的,而是通过解压cpio档案的内容到内存中的临时文件系统(tmpfs)来实现的。这种方式类似于Linux中的initramfs,并且在内核的初始化代码中处理。

boot.img包含内核镜像、ramdisk镜像和可能的设备树blob(DTB), 简单的可以通过file命令查看,或者可以解压boot.img查看里面的内容。

$ file boot.img 
boot.img: Android bootimg, kernel (0x20fecc), ramdisk (0x62c)

ramdisk包含的内容可以查看out目录中生成的ramdisk文件夹
请添加图片描述

通过 ps -ef | grep init查看init进程启动参数

barbet:/ # ps -ef | grep init
root             1     0 0 16:52:12 ?     00:00:54 init second_stage
root           554     1 0 16:52:13 ?     00:00:02 init subcontext u:r:vendor_init:s0 14

由于第一阶段 init 过程是序列化的,因此并行化启动过程的机会并不多。如果一个模块在完成第一阶段 init 时用不到,请将模块放入 vendor 或 vendor_dlkm 分区,从而将其移至第二阶段 init。
https://source.android.com/docs/core/architecture/kernel/boot-time-opt?hl=zh-cn

1.1 init源码结构

请添加图片描述

这些源码会编译出两个init(./system/bin/init和./ramdisk/init),以及一个ueventd符号链接,ueventd链接到./system/bin/init, 有着同样的md5值。

out/target/product/barbet$ md5sum ./system/bin/init ./system/bin/ueventd ./ramdisk/init
741c9721502bb629d7329a69162689e5  ./system/bin/init
741c9721502bb629d7329a69162689e5  ./system/bin/ueventd
c3b8ba9312369c6307036369539cea74  ./ramdisk/init

init启动分5个阶段:

FirstStateMain->SetupSelinux->SecondStageMain->SubcontextMain->ueventd_main

请添加图片描述

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
    __hwasan_set_error_report_callback(AsanReportCallback);
#endif
    // Boost prio which will be restored later
    setpriority(PRIO_PROCESS, 0, -20);
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }
    return FirstStageMain(argc, argv);
}

查看Android.bp, FirstStateMain对应的就是./ramdisk/init,作用是执行init第一阶段的业务。
所以如果我们需要调试init进程init_first_stage相关的源码,比如添加log,应该烧写boot.img,而不是push /system/bin/init文件,/system/bin/init对应的是init第二阶段和ueventd的业务代码。

./ramdisk/init: FirstStateMain
./system/bin/init: SetupSelinux, SecondStageMain, SubcontextMain, ueventd_main

cc_binary {
    name: "init_first_stage",
    stem: "init",
    defaults: ["init_first_stage_defaults"],

    srcs: [
        "block_dev_initializer.cpp",
        "devices.cpp",
        "first_stage_console.cpp",
        "first_stage_init.cpp",
        "first_stage_main.cpp",
        "first_stage_mount.cpp",
        "reboot_utils.cpp",
        "selabel.cpp",
        "service_utils.cpp",
        "snapuserd_transition.cpp",
        "switch_root.cpp",
        "uevent_listener.cpp",
        "util.cpp",
    ],

    static_libs: [
        "libc++fs",
        "libfs_avb",
        "libfs_mgr",
        "libfec",
        "libfec_rs",
        "libsquashfs_utils",
        "libcrypto_utils",
        "libavb",
        "liblp",
        "libcutils",
        "libbase",
        "liblog",
        "libcrypto_static",
        "libselinux",
        "libcap",
        "libgsi",
        "liblzma",
        "libunwindstack_no_dex",
        "libbacktrace_no_dex",
        "libmodprobe",
        "libext2_uuid",
        "libprotobuf-cpp-lite",
        "libsnapshot_cow",
        "libsnapshot_init",
        "update_metadata-protos",
        "libprocinfo",
    ],

    static_executable: true,
    system_shared_libs: [],

    cflags: [
        "-Wall",
        "-Wextra",
        "-Wno-unused-parameter",
        "-Werror",
        "-DALLOW_FIRST_STAGE_CONSOLE=0",
        "-DALLOW_LOCAL_PROP_OVERRIDE=0",
        "-DALLOW_PERMISSIVE_SELINUX=0",
        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
        "-DWORLD_WRITABLE_KMSG=0",
        "-DDUMP_ON_UMOUNT_FAILURE=0",
        "-DSHUTDOWN_ZERO_TIMEOUT=0",
        "-DLOG_UEVENTS=0",
        "-DSEPOLICY_VERSION=30", // TODO(jiyong): externalize the version number
    ],

    product_variables: {
        debuggable: {
            cflags: [
                "-UALLOW_FIRST_STAGE_CONSOLE",
                "-DALLOW_FIRST_STAGE_CONSOLE=1",

                "-UALLOW_LOCAL_PROP_OVERRIDE",
                "-DALLOW_LOCAL_PROP_OVERRIDE=1",

                "-UALLOW_PERMISSIVE_SELINUX",
                "-DALLOW_PERMISSIVE_SELINUX=1",

                "-UREBOOT_BOOTLOADER_ON_PANIC",
                "-DREBOOT_BOOTLOADER_ON_PANIC=1",

                "-UWORLD_WRITABLE_KMSG",
                "-DWORLD_WRITABLE_KMSG=1",

                "-UDUMP_ON_UMOUNT_FAILURE",
                "-DDUMP_ON_UMOUNT_FAILURE=1",
            ],
        },

        eng: {
            cflags: [
                "-USHUTDOWN_ZERO_TIMEOUT",
                "-DSHUTDOWN_ZERO_TIMEOUT=1",
            ],
        },
    },

    sanitize: {
        misc_undefined: ["signed-integer-overflow"],

        // First stage init is weird: it may start without stdout/stderr, and no /proc.
        hwaddress: false,
    },

    // Install adb_debug.prop into debug ramdisk.
    // This allows adb root on a user build, when debug ramdisk is used.
    required: ["adb_debug.prop"],

    ramdisk: true,

    install_in_root: true,
}

init_second_stage对应./system/bin/init和 ./system/bin/ueventd

cc_binary {
    name: "init_second_stage",
    recovery_available: true,
    stem: "init",
    defaults: ["init_defaults"],
    static_libs: ["libinit"],
    srcs: ["main.cpp"],
    symlinks: ["ueventd"],
    target: {
        platform: {
            required: [
                "init.rc",
                "ueventd.rc",
                "e2fsdroid",
                "extra_free_kbytes.sh",
                "make_f2fs",
                "mke2fs",
                "sload_f2fs",
            ],
        },
        recovery: {
            cflags: ["-DRECOVERY"],
            exclude_static_libs: [
                "libxml2",
            ],
            exclude_shared_libs: [
                "libbinder",
                "libutils",
            ],
            required: [
                "init_recovery.rc",
                "ueventd.rc.recovery",
                "e2fsdroid.recovery",
                "make_f2fs.recovery",
                "mke2fs.recovery",
                "sload_f2fs.recovery",
            ],
        },
    },
    visibility: ["//packages/modules/Virtualization/microdroid"],
}

Android.bp中
stem:用于重命名生成文件的基名。
symlinks:用于创建符号链接。
phony:用于定义伪目标,用来建立依赖关系或执行特定构建步骤。

1.2 init启动阶段

1.2.1 FirstStateMain

FirstStageMain 函数在 Android 引导过程中执行了大量的初始化工作,包括环境变量设置、文件系统挂载、内核模块加载以及控制台和调试选项的处理。

环境变量设置,部分文件系统挂载
#define CHECKCALL(x) \
    if ((x) != 0) errors.emplace_back(#x " failed", errno);

    // Clear the umask.
    umask(0);

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mkdir("/dev/dm-user", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
    // Don't expose the raw commandline to unprivileged processes.
    CHECKCALL(chmod("/proc/cmdline", 0440));
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);
    // Don't expose the raw bootconfig to unprivileged processes.
    chmod("/proc/bootconfig", 0440);
    std::string bootconfig;
    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }

    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    // This is needed for log wrapper, which gets called before ueventd runs.
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    // These below mounts are done in first stage init so that first stage mount can mount
    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
    // should be done in rc files.
    // Mount staging areas for devices managed by vold
    // See storage config details at http://source.android.com/devices/storage/
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    // /mnt/vendor is used to mount vendor-specific partitions that can not be
    // part of the vendor partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/product", 0755));

    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));

    // /second_stage_resources is used to preserve files from first to second
    // stage init
    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"))

    // First stage init stores Mainline sepolicy here.
    CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL

调用 ,之后才能够打印kernel log。

InitKernelLogging(argv)
加载内核模块
boot_clock::time_point module_start_time = boot_clock::now();
    int module_count = 0;
    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
                           want_parallel, module_count)) {
        if (want_console != FirstStageConsoleParam::DISABLED) {
            LOG(ERROR) << "Failed to load kernel modules, starting console";
        } else {
            LOG(FATAL) << "Failed to load kernel modules";
        }
    }
    if (module_count > 0) {
        auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(
                boot_clock::now() - module_start_time);
        setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
        LOG(INFO) << "Loaded " << module_count << " kernel modules took "
                  << module_elapse_time.count() << " ms";
    }

对应的log,dmesg或者logcat都可查看

行    590: 03-25 01:32:45.979     0     0 I         : c7      1 init: Loading module /lib/modules/msm_ipc_logging.ko with args ''
行    591: 03-25 01:32:45.980     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/msm_ipc_logging.ko
行    592: 03-25 01:32:45.980     0     0 I         : c7      1 init: Loading module /lib/modules/qtee_shm_bridge.ko with args ''
行    595: 03-25 01:32:45.983     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qtee_shm_bridge.ko
行    596: 03-25 01:32:45.983     0     0 I         : c7      1 init: Loading module /lib/modules/qcom-cpufreq-hw.ko with args ''
行    602: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qcom-cpufreq-hw.ko
行    603: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loading module /lib/modules/qcom_hwspinlock.ko with args ''
行    604: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qcom_hwspinlock.ko
行    605: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loading module /lib/modules/smem.ko with args ''
行    606: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/smem.ko
行    607: 03-25 01:32:45.997     0     0 I         : c7      1 init: Loading m
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

techfuture

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值