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
这些源码会编译出两个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:用于定义伪目标,用来建立依赖关系或执行特定构建步骤。
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(
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 module /lib/modules/msm_minidump.ko with args ''
行 609: 03-25 01:32:45.997 0 0 I : c7 1 init: Loaded kernel module /lib/modules/msm_minidump.ko
行 610: 03-25 01:32:45.997 0 0 I : c7 1 init: Loading module /lib/modules/watchdog_v2.ko with args ''
行 613: 03-25 01:32:45.997 0 0 I : c7 1 init: Loaded kernel module /lib/modules/watchdog_v2.ko
行 614: 03-25 01:32:45.997 0 0 I : c7 1 init: Loading module /lib/modules/early_random.ko with args ''
行 616: 03-25 01:32:45.998 0 0 I : c7 1 init: Loaded kernel module /lib/modules/early_random.ko
行 617: 03-25 01:32:45.998 0 0 I : c7 1 init: Loading module /lib/modules/qcom-pdc.ko with args ''
行 618: 03-25 01:32:45.998 0 0 I : c7 1 init: Loaded kernel module /lib/modules/qcom-pdc.ko
行 619: 03-25 01:32:45.998 0 0 I : c7 1 init: Loading module /lib/modules/cmd-db.ko with args ''
行 620: 03-25 01:32:45.998 0 0 I : c7 1 init: Loaded kernel module /lib/modules/cmd-db.ko
行 621: 03-25 01:32:45.998 0 0 I : c7 1 init: Loading module /lib/modules/qcom_rpmh.ko with args ''
行 680: 03-25 01:32:46.075 0 0 I : c7 1 init: Loaded kernel module /lib/modules/qcom_rpmh.ko
行 681: 03-25 01:32:46.075 0 0 I : c7 1 init: Loading module /lib/modules/phy-qcom-ufs.ko with args ''
行 682: 03-25 01:32:46.075 0 0 I : c7 1 init: Loaded kernel module /lib/modules/phy-qcom-ufs.ko
行 683: 03-25 01:32:46.075 0 0 I : c7 1 init: Loading module /lib/modules/phy-qcom-ufs-qrbtc-sdm845.ko with args ''
行 684: 03-25 01:32:46.076 0 0 I : c7 1 init: Loaded kernel module /lib/modules/phy-qcom-ufs-qrbtc-sdm845.ko
...
...
...
行 2073: 03-25 01:32:46.667 0 0 I : c7 1 init: Loaded kernel module /lib/modules/ebtables.ko
行 2074: 03-25 01:32:46.667 0 0 I : c7 1 init: Loading module /lib/modules/ebtable_broute.ko with args ''
行 2075: 03-25 01:32:46.668 0 0 I : c7 1 init: Loaded kernel module /lib/modules/ebtable_broute.ko
行 2076: 03-25 01:32:46.668 0 0 I : c7 1 init: Loading module /lib/modules/sctp.ko with args ''
行 2078: 03-25 01:32:46.672 0 0 I : c7 1 init: Loaded kernel module /lib/modules/sctp.ko
行 2079: 03-25 01:32:46.672 0 0 I : c7 1 init: Loading module /lib/modules/sctp_diag.ko with args ''
行 2080: 03-25 01:32:46.673 0 0 I : c7 1 init: Loaded kernel module /lib/modules/sctp_diag.ko
行 2081: 03-25 01:32:46.673 0 0 I : c7 1 init: Loading module /lib/modules/qrtr-smd.ko with args ''
行 2082: 03-25 01:32:46.673 0 0 I : c7 1 init: Loaded kernel module /lib/modules/qrtr-smd.ko
行 2083: 03-25 01:32:46.673 0 0 I : c7 1 init: Loaded 220 kernel modules took 697 ms
if (ForceNormalBoot(cmdline, bootconfig)) {
mkdir("/first_stage_ramdisk", 0755);
PrepareSwitchRoot();
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
SwitchRoot("/first_stage_ramdisk");
}
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
对应log
行 2086: 03-25 01:32:46.674 0 0 I : c7 1 init: Switching root to '/first_stage_ramdisk'
行 2088: 03-25 01:32:46.674 0 0 I : c7 1 init: [libfs_mgr]ReadFstabFromDt(): failed to read fstab from dt
行 2089: 03-25 01:32:46.674 0 0 I : c7 1 init: Using Android DT directory /proc/device-tree/firmware/android/
行 2090: 03-25 01:32:46.687 0 0 I : c7 1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/by-name/metadata
行 2096: 03-25 01:32:46.689 0 0 I : c7 1 init: [libfs_mgr]check_fs(): mount(/dev/block/by-name/metadata,/metadata,ext4)=0: Success
行 2097: 03-25 01:32:46.689 0 0 I : c7 1 init: [libfs_mgr]umount_retry(): unmount(/metadata) succeeded
行 2098: 03-25 01:32:46.689 0 0 I : c7 1 init: [libfs_mgr]Running /system/bin/e2fsck on /dev/block/sda15
行 2110: 03-25 01:32:46.699 0 0 I : c7 1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/by-name/metadata
行 2113: 03-25 01:32:46.700 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/by-name/metadata,target=/metadata,type=ext4)=0: Success
行 2114: 03-25 01:32:46.702 0 0 I : c7 1 init: [libfs_mgr]Created logical partition system_a on device /dev/block/dm-0
行 2115: 03-25 01:32:46.703 0 0 I : c7 1 init: [libfs_mgr]Created logical partition system_b on device /dev/block/dm-1
行 2116: 03-25 01:32:46.703 0 0 I : c7 1 init: [libfs_mgr]Created logical partition product_a on device /dev/block/dm-2
行 2117: 03-25 01:32:46.703 0 0 I : c7 1 init: [libfs_mgr]Created logical partition product_b on device /dev/block/dm-3
行 2118: 03-25 01:32:46.703 0 0 I : c7 1 init: [libfs_mgr]Created logical partition system_ext_a on device /dev/block/dm-4
行 2119: 03-25 01:32:46.704 0 0 I : c7 1 init: [libfs_mgr]Created logical partition system_ext_b on device /dev/block/dm-5
行 2120: 03-25 01:32:46.704 0 0 I : c7 1 init: [libfs_mgr]Created logical partition vendor_a on device /dev/block/dm-6
行 2121: 03-25 01:32:46.704 0 0 I : c7 1 init: [libfs_mgr]Created logical partition vendor_b on device /dev/block/dm-7
行 2122: 03-25 01:32:46.704 0 0 I : c7 1 init: DSU not detected, proceeding with normal boot
行 2123: 03-25 01:32:46.705 0 0 E : c7 1 init: [libfs_avb]Invalid hash size:
行 2124: 03-25 01:32:46.705 0 0 E : c7 1 init: [libfs_avb]Failed to verify vbmeta digest
行 2125: 03-25 01:32:46.705 0 0 I : c7 1 init: [libfs_avb]Returning avb_handle with status: VerificationDisabled
行 2126: 03-25 01:32:46.705 0 0 I : c7 1 init: [libfs_avb]AVB HASHTREE disabled on: /system
行 2127: 03-25 01:32:46.706 0 0 I : c7 1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-0
行 2129: 03-25 01:32:46.707 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/dm-0,target=/system,type=ext4)=0: Success
行 2130: 03-25 01:32:46.707 0 0 I : c7 1 init: Switching root to '/system'
行 2131: 03-25 01:32:46.708 0 0 I : c7 1 init: [libfs_avb]AVB HASHTREE disabled on: /system_ext
行 2132: 03-25 01:32:46.708 0 0 I : c7 1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-4
行 2134: 03-25 01:32:46.709 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/dm-4,target=/system_ext,type=ext4)=0: Success
行 2135: 03-25 01:32:46.709 0 0 I : c7 1 init: [libfs_avb]AVB HASHTREE disabled on: /vendor
行 2136: 03-25 01:32:46.709 0 0 I : c7 1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-6
行 2138: 03-25 01:32:46.711 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/dm-6,target=/vendor,type=ext4)=0: Success
行 2139: 03-25 01:32:46.711 0 0 I : c7 1 init: [libfs_avb]AVB HASHTREE disabled on: /product
行 2140: 03-25 01:32:46.711 0 0 I : c7 1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-2
行 2142: 03-25 01:32:46.712 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/dm-2,target=/product,type=ext4)=0: Success
行 2143: 03-25 01:32:46.725 0 0 I : c7 1 init: [libfs_mgr]Created logical partition scratch on device /dev/block/dm-8
行 2163: 03-25 01:32:46.766 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/dm-8,target=/mnt/scratch,type=f2fs)=0: Success
行 2164: 03-25 01:32:46.767 0 0 I : c7 1 init: [libfs_mgr]umount(/mnt/scratch)
行 2182: 03-25 01:32:46.773 0 0 I : c7 1 init: [libfs_mgr]__mount(source=/dev/block/dm-8,target=/mnt/scratch,type=f2fs)=0: Success
行 2208: 03-25 01:32:46.779 0 0 I : c7 1 init: [libfs_mgr]__mount(source=overlay,target=/system,type=overlay,upperdir=/mnt/scratch/overlay/system/upper)=0
行 2237: 03-25 01:32:46.784 0 0 I : c6 81 [drm:sde_dbg_init:4755] evtlog_status: enable:11, panic:1, dump:2
行 2243: 03-25 01:32:46.784 0 0 I : c6 81 dsi_panel_switch_init: Panel switch is not supported
行 2249: 03-25 01:32:46.785 0 0 I : c7 1 init: [libfs_mgr]__mount(source=overlay,target=/system_ext,type=overlay,upperdir=/mnt/scratch/overlay/system_ext/upper)=0
行 2273: 03-25 01:32:46.791 0 0 I : c7 1 init: [libfs_mgr]__mount(source=overlay,target=/vendor,type=overlay,upperdir=/mnt/scratch/overlay/vendor/upper)=0
行 2286: 03-25 01:32:46.797 0 0 I : c7 1 init: [libfs_mgr]__mount(source=overlay,target=/product,type=overlay,upperdir=/mnt/scratch/overlay/product/upper)=0
挂载实际文件系统后,在FirstStageMain尾部调用第二阶段的 init 进程(/system/bin/init
)。
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
execv(path, const_cast(args));
SetupSelinux
函数在 Android 引导过程中扮演了设置和加载 SELinux(Security-Enhanced Linux)策略的关键角色。SELinux 是一种强制访问控制(MAC)机制,用于增强系统安全性。在 Android 系统启动过程中,SetupSelinux
函数确保系统正确加载和应用 SELinux 策略,从而保护系统免受潜在的安全威胁。
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
MountMissingSystemPartitions();
SelinuxSetupKernelLogging();
LOG(INFO) << "Opening SELinux policy";
PrepareApexSepolicy();
// Read the policy before potentially killing snapuserd.
std::string policy;
ReadPolicy(&policy);
CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
// Kill the old snapused to avoid audit messages. After this we cannot
// read from /system (or other dynamic partitions) until we call
// FinishTransition().
snapuserd_helper->StartTransition();
}
LoadSelinuxPolicy(policy);
if (snapuserd_helper) {
// Before enforcing, finish the pending snapuserd transition.
snapuserd_helper->FinishTransition();
snapuserd_helper = nullptr;
}
// This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
// needed to transition files from tmpfs to *_contexts_file context should not be granted to
// any process after selinux is set into enforcing mode.
if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
}
SelinuxSetEnforcement();
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
// but other file systems do. In particular, this is needed for ramdisks such as the
// recovery image for A/B devices.
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast(args));
// execv() only returns if an error happened, in which case we
// panic and never return from this function.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
最后是再调用进入 /system/bin/init
,进入 second_stage
。
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast(args));
对应的log
行 2330: 03-25 01:32:46.870 0 0 I : c2 1 init: Opening SELinux policy
行 2331: 03-25 01:32:46.873 0 0 I : c2 1 init: Falling back to standard signature check. TODO implementent support for fsverity SEPolicy.
行 2332: 03-25 01:32:46.873 0 0 I : c2 1 init: Error: Apex SEPolicy failed signature check
行 2333: 03-25 01:32:46.873 0 0 I : c2 1 init: Loading APEX Sepolicy from /system/etc/selinux/apex/SEPolicy.zip
行 2334: 03-25 01:32:46.873 0 0 E : c2 1 init: Failed to open package /system/etc/selinux/apex/SEPolicy.zip: No such file or directory
行 2335: 03-25 01:32:46.879 0 0 I : c2 1 init: Loading SELinux policy
SecondStageMain
函数在 Android 启动过程中负责初始化系统的第二阶段。它执行一系列关键操作来配置系统环境、加载必要的服务和文件系统,并设置 SELinux 安全策略。详细解释如下
int SecondStageMain(int argc, char** argv) {
"安装重启信号处理程序"
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// No threads should be spin up until signalfd
// is registered. If the threads are indeed required,
// each of these threads _should_ make sure SIGCHLD signal
// is blocked. See b/223076262
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
"重定向标准输入输出和初始化内核日志"
SetStdioToDevNull(argv);
InitKernelLogging(argv);
LOG(INFO) << "debugboot-1---init second stage started!";
LOG(INFO) << "init second stage started!";
// Update $PATH in the case the second stage init is newer than first stage init, where it is
// first set.
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
}
// Init should not crash because of a dependence on any other process, therefore we ignore
// SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
// is inherited across exec, but custom signal handlers are not. Since we do not want to
// ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
{
struct sigaction action = {.sa_flags = SA_RESTART};
action.sa_handler = [](int) {};
sigaction(SIGPIPE, &action, nullptr);
}
"设置 init 及其子进程的 OOM 分数调整"
// Set init and its forked children's oom_adj.
if (auto result =
WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
!result.ok()) {
LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
<< " to /proc/1/oom_score_adj: " << result.error();
}
// 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.
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
"创建一个文件以指示引导进程正在进行。"
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
"处理调试属性,如果设备解锁并设置了调试属性,则加载调试属性,否则卸载调试 RAM 盘。"
// 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");
bool load_debug_prop = false;
if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
load_debug_prop = "true"s == force_debuggable_env;
}
unsetenv("INIT_FORCE_DEBUGGABLE");
// Umount the debug ramdisk so property service doesn't read .prop files from there, when it
// is not meant to.
if (!load_debug_prop) {
UmountDebugRamdisk();
}
"初始化属性服务。"
PropertyInit();
"卸载第二阶段资源和调试 RAM 盘"
// Umount second stage resources after property service has read the .prop files.
UmountSecondStageRes();
// Umount the debug ramdisk after property service has read the .prop files when it means to.
if (load_debug_prop) {
UmountDebugRamdisk();
}
"挂载第二阶段初始化所需的额外文件系统。"
// Mount extra filesystems required during second stage init
MountExtraFilesystems();
"设置 SELinux 内核日志记录,初始化 SELinux 标签并恢复 SELinux 上下文。"
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
"初始化 Epoll 和信号处理"
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
StartPropertyService(&property_fd);
"记录启动阶段的时间。"
// Make the time that init stages started available for bootstat to log.
RecordStageBoottimes(start_time);
"设置 AVB 版本属性并清除环境变量。"
// Set libavb version for Framework-only OTA match in Treble build.
if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
SetProperty("ro.boot.avb_version", avb_version);
}
unsetenv("INIT_AVB_VERSION");
"挂载 vendor 覆盖文件系统,导出 OEM 锁定状态,初始化挂载处理程序,设置 USB 控制器和内核版本。"
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
MountHandler mount_handler(&epoll);
SetUsbController();
SetKernelVersion();
"加载内置函数映射并设置挂载命名空间,初始化子上下文。"
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespaces failed";
}
InitializeSubcontext();
"加载启动脚本"
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
"设置 GSI(通用系统镜像)状态属性"
// Make the GSI status available before scripts start running.
auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
SetProperty(gsi::kGsiBootedProp, is_running);
auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
SetProperty(gsi::kGsiInstalledProp, is_installed);
"通过队列一些内置动作和事件触发来完成 Android 系统启动过程中的一系列关键配置和初始化任务"
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
LOG(DEBUG) << "debugboot----trigger early-init";
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result {
for (const auto& svc : ServiceList::GetInstance()) {
keychords.Register(svc->keycodes());
}
keychords.Start(&epoll, HandleKeychord);
return {};
},
"KeychordInit");
LOG(DEBUG) << "debugboot----trigger init";
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
// Restore prio before main loop
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional{kDiagnosticTimeout};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!IsShuttingDown()) {
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
epoll_timeout = std::chrono::ceil(
*next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
auto pending_functions = epoll.Wait(epoll_timeout);
if (!pending_functions.ok()) {
LOG(ERROR) << pending_functions.error();
} else if (!pending_functions->empty()) {
// We always reap children before responding to the other pending functions. This is to
// prevent a race where other daemons see that a service has exited and ask init to
// start it again via ctl.start before init has reaped it.
ReapAnyOutstandingChildren();
for (const auto& function : *pending_functions) {
(*function)();
}
} else if (Service::is_exec_service_running()) {
static bool dumped_diagnostics = false;
std::chrono::duration waited =
std::chrono::steady_clock::now() - Service::exec_service_started();
if (waited >= kDiagnosticTimeout) {
LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
<< " without SIGCHLD";
if (!dumped_diagnostics) {
DumpPidFds("exec service opened: ", Service::exec_service_pid());
std::string status_file =
"/proc/" + std::to_string(Service::exec_service_pid()) + "/status";
DumpFile("exec service: ", status_file);
dumped_diagnostics = true;
LOG(INFO) << "Attempting to handle any stuck SIGCHLDs...";
HandleSignalFd(true);
}
}
}
if (!IsShuttingDown()) {
HandleControlMessages();
SetUsbController();
}
}
return 0;
}
system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
通常位于/system/etc/init/hw/init.rc,/system/etc/init, vendor/etc/init,其他如system_ext,odm,product不一定包含rc文件,比如pixel5a Android13。
设备目录下查看相关目录
pixel5a Android13源码out编译目录下vendor/etc/init是不存在的,因为vendor部分闭源,vendor.img也是预编译的镜像。
$ grep "qcrild.rc" ./vendor -R
Binary file ./vendor/google_devices/barbet/proprietary/vendor.img matches
$ md5sum vendor/google_devices/barbet/proprietary/vendor.img
d681d7d0d9ce0cf5f64a25b6344815cf vendor/google_devices/barbet/proprietary/vendor.img
$ md5sum out/target/product/barbet/vendor.img
d681d7d0d9ce0cf5f64a25b6344815cf out/target/product/barbet/vendor.img
加载完所有启动脚本后,通过QueueEventTrigger来触发进程启动,按关系链启动所有进程。
int SecondStageMain(int argc, char** argv) {
...
am.QueueEventTrigger("early-init");
...
am.QueueEventTrigger("init");
...
am.QueueEventTrigger("late-init");
...
}
on nonencrypted触发流程
on nonencrypted
class_start main
class_start late_start
/system/core/init/builtins.cpp
static Result queue_fs_event(int code, bool userdata_remount) {
if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
...
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
...
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
...
}
...
return Error() << "Invalid code: " << code;
}
SubcontextMain启动过程
init.cpp: SecondStageMain()->InitializeSubcontext()
void InitializeSubcontext() {
if (IsMicrodroid()) {
LOG(INFO) << "Not using subcontext for microdroid";
return;
}
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
subcontext.reset(
new Subcontext(std::vector{"/vendor", "/odm"}, kVendorContext));
}
}
subcontext.cpp: new Subcontext()
class Subcontext {
public:
Subcontext(std::vector[std::string](std::string) path_prefixes, std::string context, bool host = false)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
if (!host) {
Fork();
}
}
subcontext.cpp: Subcontext()->Fork ->fork() -> execv()
void Subcontext::Fork() {
unique_fd subcontext_socket;
if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
return;
}
auto result = fork();
if (result == -1) {
LOG(FATAL) << "Could not fork subcontext";
} else if (result == 0) {
socket_.reset();
// We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
// in the subcontext process after we exec.
int child_fd = dup(subcontext_socket); // NOLINT(android-cloexec-dup)
if (child_fd < 0) {
PLOG(FATAL) << "Could not dup child_fd";
}
// We don't switch contexts if we're running the unit tests. We don't use std::optional,
// since we still need a real context string to pass to the builtin functions.
if (context_ != kTestContext) {
if (setexeccon(context_.c_str()) < 0) {
PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
}
}
#if defined(__ANDROID__)
// subcontext init runs in "default" mount namespace
// so that it can access /apex/*
if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
LOG(FATAL) << "Could not switch to \"default\" mount namespace: " << result.error();
}
#endif
auto init_path = GetExecutablePath();
auto child_fd_string = std::to_string(child_fd);
const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
child_fd_string.c_str(), nullptr};
execv(init_path.data(), const_cast(args));
PLOG(FATAL) << "Could not execv subcontext init";
} else {
subcontext_socket.reset();
pid_ = result;
LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
}
}
ueventd 是 Android 系统中负责处理内核 uevent 消息的守护进程,负责处理由内核发出的 uevent 消息,uevent 消息用于通知用户空间硬件事件。 ueventd 能够创建设备节点、设置权限、管理符号链接,并在设备状态变化时进行相应的处理,如设备插入或移除。它在系统启动时执行冷启动,确保所有设备都被正确处理。这个进程在确保 Android 设备的硬件资源可用性和权限管理方面起着关键作用。
init.rc 中start ueventd
on early-init
...
start ueventd
...
exec_start apexd-bootstrap
...
service ueventd /system/bin/ueventd
class core
critical
onrestart restart healthd
shutdown critical
/system/bin/ueventd软链接到system/bin/init
barbet:/ # ls -la system/bin/ueventd
lrwxr-xr-x 1 root shell 4 2024-06-19 20:05 system/bin/ueventd -> init
init.cpp中调用ueventd_main
DeviceHandler::HandleUevent
函数在接收到 uevent
事件后,首先根据事件类型修复系统权限,然后根据子系统类型设置设备路径和符号链接,并创建相应的设备节点。对于特殊设备(如 /dev/ashmem
),进行额外处理。这是 ueventd
在 Android 系统中管理设备节点和处理硬件事件的核心部分。
system/core/init/ueventd.cpp
int ueventd_main(int argc, char** argv) {
// 设置 umask 为 000 以确保创建文件时不受 umask 影响
umask(000);
// 初始化日志记录
android::base::InitLogging(argv, &android::base::KernelLogger);
// 设置 SELinux 日志记录
SelinuxSetupKernelLogging();
// 初始化 SELinux 标签
SelabelInitialize();
// 创建 uevent 处理器列表
std::vector> uevent_handlers;
// 获取 ueventd 配置
auto ueventd_configuration = GetConfiguration();
// 添加设备处理器
uevent_handlers.emplace_back(std::make_unique(
std::move(ueventd_configuration.dev_permissions),
std::move(ueventd_configuration.sysfs_permissions),
std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
// 添加固件处理器
uevent_handlers.emplace_back(std::make_unique(
std::move(ueventd_configuration.firmware_directories),
std::move(ueventd_configuration.external_firmware_handlers)));
// 如果配置启用了 modalias 处理,则添加 modalias 处理器
if (ueventd_configuration.enable_modalias_handling) {
std::vector base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
uevent_handlers.emplace_back(std::make_unique(base_paths));
}
// 创建 uevent 监听器
UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
// 如果未完成冷启动,则进行冷启动
if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
ColdBoot cold_boot(uevent_listener, uevent_handlers,
ueventd_configuration.enable_parallel_restorecon,
ueventd_configuration.parallel_restorecon_dirs);
cold_boot.Run();
}
// 冷启动完成后的处理
for (auto& uevent_handler : uevent_handlers) {
uevent_handler->ColdbootDone();
}
// 忽略 SIGCHLD 信号并处理任何挂起的子进程
signal(SIGCHLD, SIG_IGN);
while (waitpid(-1, nullptr, WNOHANG) > 0) {
}
// 恢复进程优先级
setpriority(PRIO_PROCESS, 0, 0);
// 进入 uevent 监听循环,处理 uevent 消息
uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
for (auto& uevent_handler : uevent_handlers) {
uevent_handler->HandleUevent(uevent);
}
return ListenerAction::kContinue;
});
return 0;
}
static UeventdConfiguration GetConfiguration() {
auto hardware = android::base::GetProperty("ro.hardware", "");
std::vector legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
"/ueventd." + hardware + ".rc"};
std::vector canonical{"/system/etc/ueventd.rc"};
if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
// TODO: Remove these legacy paths once Android S is no longer supported.
canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
} else {
// Warn if newer device is using legacy paths.
for (const auto& path : legacy_paths) {
if (access(path.c_str(), F_OK) == 0) {
LOG(FATAL_WITHOUT_ABORT)
<< "Legacy ueventd configuration file detected and will not be parsed: "
<< path;
}
}
}
return ParseConfig(canonical);
}
system/core/init/devices.cpp
void DeviceHandler::HandleUevent(const Uevent& uevent) {
if (uevent.action == "add" || uevent.action == "change" ||
uevent.action == "bind" || uevent.action == "online") {
FixupSysPermissions(uevent.path, uevent.subsystem);
}
// if it's not a /dev device, nothing to do
if (uevent.major < 0 || uevent.minor < 0) return;
std::string devpath;
std::vector links;
bool block = false;
if (uevent.subsystem == "block") {
block = true;
devpath = "/dev/block/" + Basename(uevent.path);
if (StartsWith(uevent.path, "/devices")) {
links = GetBlockDeviceSymlinks(uevent);
}
} else if (const auto subsystem =
std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
subsystem != subsystems_.cend()) {
devpath = subsystem->ParseDevPath(uevent);
} else if (uevent.subsystem == "usb") {
if (!uevent.device_name.empty()) {
devpath = "/dev/" + uevent.device_name;
} else {
// This imitates the file system that would be created
// if we were using devfs instead.
// Minors are broken up into groups of 128, starting at "001"
int bus_id = uevent.minor / 128 + 1;
int device_id = uevent.minor % 128 + 1;
devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
} else if (StartsWith(uevent.subsystem, "usb")) {
// ignore other USB events
return;
} else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
} else {
devpath = "/dev/" + Basename(uevent.path);
}
mkdir_recursive(Dirname(devpath), 0755);
HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
// Duplicate /dev/ashmem device and name it /dev/ashmem.
// TODO(b/111903542): remove once all users of /dev/ashmem are migrated to libcutils API.
HandleAshmemUevent(uevent);
}
Zygote 是 Android 系统中的关键进程,负责启动 Java 虚拟机 (JVM) 并创建系统服务进程和应用进程。Zygote 启动流程复杂且涉及多个组件,以下是详细的 Zygote 启动流程及其主要步骤。
system/etc/init/hw/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
# NOTE: If the wakelock name here is changed, then also
# update it in SystemSuspend.cpp
onrestart write /sys/power/wake_lock zygote_kwl
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
init
进程执行 /system/bin/app_process
,这个二进制文件是启动 Zygote 的入口点。app_process
在启动时,解析参数并调用 com.android.internal.os.ZygoteInit
类的 main
方法。frameworks/base/cmds/app_process/app_main.cpp
call AndroidRuntime.cpp runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
int main(int argc, char* const argv[])
{
...
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
}
}
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
}
...
}
frameworks/base/core/jni/AndroidRuntime.cpp
中的start
方法
void AndroidRuntime::start(const char* className, const Vector& options) {
...
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
...
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
...
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
if (startMeth == NULL) {
...
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
if (env->ExceptionCheck()) {
...
}
}
}
...
}
ZygoteInit
类的 main
方法是 Zygote 进程的真正入口。main
方法中,首先进行一些初始化操作,如设置性能参数、启动本地服务等。frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
...
// Preload classes and resources
preload();
// Start SystemServer
if (startSystemServer) {
forkSystemServer(abiList, zygoteSocketName, zygoteServer);
}
// Accept socket connections
caller = zygoteServer.runSelectLoop(abiList);
...
}
main
方法中,调用 preload
方法预加载一些类和资源,以提高后续应用进程启动的效率。frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
中的 preload
方法static void preload(TimingsTraceLog bootTimingsTraceLog) {
...
beginPreload();
preloadClasses();
cacheNonBootClasspathClassLoaders();
preloadResources();
nativePreloadAppProcessHALs();
maybePreloadGraphicsDriver();
preloadSharedLibraries();
preloadTextResources();
WebViewFactory.prepareWebViewInZygote();
endPreload();
...
}
forkSystemServer
方法启动 SystemServer,这是一个关键的系统服务进程,负责启动其他系统服务。frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
中的 forkSystemServer
方法private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
...
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
...
}
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
中的 runSelectLoop
方法Runnable runSelectLoop(String abiList) {
ArrayList socketFDs = new ArrayList<>();
ArrayList peers = new ArrayList<>();
...
if (pollIndex == 0) {
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
final Runnable command = connection.processCommand(this, multipleForksOK);
}
}
...
}
至此,zygote可以接受应用进程创建请求。
当用户点击应用图标或系统有其他启动应用的需求时,会触发启动应用的请求。这一请求通常由 ActivityManagerService (AMS) 处理。AMS 会与 Zygote 进程通信,要求它 fork 出一个新的应用进程。
AMS 通过 ZygoteProcess
类与 Zygote 进程通信。ZygoteProcess
通过 socket 向 Zygote 发送启动新应用的请求。以下是相关代码片段:
framework/base/core/java/android/os/ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(...) {
// 向 argsForZygote 添加启动应用所需的参数
...
synchronized(mLock) {
// The USAP pool can not be used if the application will not use the systems graphics
// driver. If that driver is requested use the Zygote application start path.
// 通过 Zygote socket 向 Zygote 进程发送启动请求
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
zygotePolicyFlags,
argsForZygote);
...
}
}
Zygote 进程的 runSelectLoop
方法在接收到新请求后,会调用 ZygoteConnection
类的 processOneCommand
方法处理请求。
framework/base/core/java/com/android/internal/os/ZygoteServer.java
Runnable runSelectLoop(String abiList) {
ArrayList socketFDs = new ArrayList<>();
ArrayList peers = new ArrayList<>();
...
if (pollIndex == 0) {
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
final Runnable command = connection.processCommand(this, multipleForksOK);
}
}
...
}
在 ZygoteConnection
类中,processOneCommand
方法会根据 AMS 传递的参数 fork 一个新的进程。
framework/base/core/java/com/android/internal/os/ZygoteConnection.java
Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
// fork 新进程
if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
|| !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
// Continue using old code for now. TODO: Handle these cases in the other path.
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {
// in child
// 子进程逻辑,启动应用
return handleChildProc(parsedArgs, childPipeFd,
parsedArgs.mStartChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
// 父进程逻辑,返回子进程 pid 给 AMS
handleParentProc(pid, serverPipeFd);
return null;
}
}
}
}
在子进程中,handleChildProc
方法会启动应用的入口类 ActivityThread
。
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
/*
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
*/
closeSocket();
Zygote.setAppProcessName(parsedArgs, TAG);
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
// 启动应用
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
return ZygoteInit.childZygoteInit(
parsedArgs.mRemainingArgs /* classLoader */);
}
}
}
ZygoteInit.zygoteInit
方法会调用 RuntimeInit
类初始化应用的运行环境,然后调用 ActivityThread
的 main
方法启动应用。
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
/framework/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
...
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
...
}
最终,ActivityThread.main
方法被调用,应用进入正常的运行状态。
framework/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
// 初始化应用的主线程
ActivityThread thread = new ActivityThread();
thread.attach(false);
}
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
中 forkSystemServer
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
String[] args = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
...
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
...
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
}
framework/base/core/java/com/android/internal/os/Zygote.java
static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
ZygoteHooks.preFork();
int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
ZygoteHooks.postForkCommon();
return pid;
}
nativeForkSystemServer
framework/base/core/jni/com_android_internal_os_Zygote.cpp
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities) {
...
pid_t pid = zygote::ForkCommon(env, true, fds_to_close, fds_to_ignore,true);
...
}
ForkCommon
pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector& fds_to_close,
const std::vector& fds_to_ignore,
bool is_priority_fork,
bool purge) {
...
pid_t pid = fork();
...
return pid;
}
执行forkSystemServer fork 出来的子进程中,handleSystemServerProcess
方法负责启动 SystemServer
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
中 handleSystemServerProcess
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
String[] args = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
...
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
...
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
}
handleSystemServerProcess
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
...
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
/* should never reach here */
}
zygoteInit
方法最终会调用 RuntimeInit.applicationInit
方法,该方法会根据传递的参数启动 SystemServer
:
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
findStaticMain
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
...
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
...
}
SystemServer
类的 main
方法是 system_server
进程的入口点:
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
// 初始化系统上下文
createSystemContext();
// 启动引导服务,包括 Installer、PowerManager 等
startBootstrapServices();
//启动核心服务,包括 BatteryService、UsageStatsService 等
startCoreServices();
//启动其他服务,包括 WindowManagerService、ActivityManagerService 等
startOtherServices();
...
}