首页 > 系统相关 >Android开机流程-从Init进程启动到进入Android桌面

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

时间:2024-09-04 13:23:29浏览次数:14  
标签:ramdisk CHECKCALL Init module cpp init 开机 Android stage

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

标签:ramdisk,CHECKCALL,Init,module,cpp,init,开机,Android,stage
From: https://blog.csdn.net/techfuture/article/details/141889195

相关文章

  • Android使用addr2line分析Native Crash
    NDK提供的工具将函数地址解析为具体的函数名和行数才能进一步分析问题。常用的地址转换工具有addr2line、ndk-stack等,个人比较喜欢addr2line,所以接下来介绍下该工具的基本使用方式日常使用过程中,只需要关注-C-f-e三个参数即可//-C:Demangle函数名//-f:显示函数名//......
  • Android Gradle 插件的说明
    1、前天运行好好的项目,今天运行就报错:这个意思是Gradle版本低了这个意思是Gradleplugin(8.5.1)最高的compileSdk=34,用了35,就不对,因为一开始我们安装的就是35的版本,我们可以安装下34,然后用34就可以了。2、GradlePlugin这是一种写法,我们也可以这样,用alias,用一个文件......
  • android kotlin基础复习—for while do...while
    1、新建一个文件kt:2、循环的几种用法:forwhiledo...whilefor:println("----for使用-----")valitems=listOf("apple","banana","kiwi")for(iteminitems){println(item)}for(indexinitems.indic......
  • 基于Android的小学数学游戏App的开发与设计-计算机毕业设计源码+LW文档
    摘 要利用了现代科技手段,将传统的小学数学教育与游戏娱乐相结合,为孩子们创造了一个更加轻松、有趣的学习环境。通过游戏化的方式,孩子们能够在玩乐中掌握数学知识,提升逻辑思维能力,激发学习兴趣。这种寓教于乐的学习方式不仅能够减轻孩子们的学习压力,还能有效提高学习效果,为他们今......
  • 专业排版设计软件Affinity Publisher for Mac
    AffinityPublisher是一款专为Mac操作系统开发的功能强大的专业排版设计软件。软件下载地址主要特点包括:丰富功能与直观界面:作为Affinity系列软件之一,它旨在为用户提供出色的排版设计工具。能轻松创建书籍、杂志、宣传册、海报等出版物,拥有丰富的排版工具,涵盖文本处理......
  • 深入理解Android Activity的四种LaunchMode
            在Android开发中,Activity的启动模式(LaunchMode)是控制Activity实例创建、复用及在任务(Task)中排列方式的重要机制。理解并掌握这些模式对于构建高效、流畅的用户体验至关重要。本文将详细探讨standard、singleTop、singleTask和singleInstance这四种启动模式,并通......
  • BroadcastReceiver 广播-Android四大组件 一文精讲
    目录1.广播用途与机制1.1什么时候用broadcast?1.2原理图解2.注册广播2.1静态注册2.2动态注册2.3二者区别与联系同:异:3.接受广播3.1接收系统广播3.2接收自定义附带值广播4.发送自定义广播4.1发送无序广播4.2发送有序广播4.3发送应用程序内部广播1.广播用途与......
  • Android开发 - BitmapFactory 类解码图像文件并转换为 Bitmap 对象与 BitmapFactory.O
    BitmapFactory是什么BitmapFactory用于解码图像文件,并将它们转换为Bitmap对象。Bitmap是用来表示图像的基本类,它是一个位图的抽象表示。BitmapFactory提供了一组静态方法,这些方法可以用来将各种图像文件格式(如PNG、JPEG、WEBP等)解码成Bitmap对象BitmapFactory的好......
  • Android经典实战之窗口和WindowManager
    本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点在Android开发中,“窗口”是一个非常基础且重要的概念。窗口通常用于承载和显示用户界面内容。了解窗口的工作机制,以及如何管理窗口,对于开发复杂的和用户体验良好的应用程序至......
  • Android Activity启动过程全解析
     https://blog.csdn.net/tenggangren/article/details/50925740 App与AMS通过Binder进行IPC通信,AMS(SystemServer进程)与zygote通过Socket进行IPC通信。一个App的程序入口到底是什么?是ActivityThread.main()。 1.ActivityManagerServices,简称AMS,服务端对象,负责系统中所有A......