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