开机有好几种方式启动,本文主要讲的是按Power键开机流程。
本文参考AOSP 12原生代码,链接为:AOSP 12 Searchhttp://aospxref.com/android-12.0.0_r3/
目录
1. BootLoader加载
2. kernel启动
3. init进程启动
3.1 FirstStageMain
3.1.1 init若崩溃会重新启动BootLoader
3.1.2 创建以及挂载文件系统
3.1.3 kernel log初始化
3.1.4 加载/lib/moudles/下的ko文件, 打开串口log
3.1.5 拷贝ramdisk prop文件,创建new ramdisk, 删除old ramdisk
3.1.6 重新执行init进程并携带参数selinux_setup
1. BootLoader加载
当按下设备电源键时,最先运行的就是 bootloader(固化在ROM的程序),bootloader 的主要作用就是硬件设备(如 CPU、flash、内存)的初始化并加载到RAM,通过建立内存空间映射,为装载 Linux 内核做好准备,。如果 bootloader 在运行期间,按下预定义的组合按键,可以进入系统fastboot模式 或者 Receiver 模式。
1)当用户按下开机键时,引导芯片代码开始从预定义的地方(固定在ROM中)开始执行,加载BootLoader到内存中执行。
2)BootLoader是在操作系统内核运行之前运行的一段小程序,通过这段小程序初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境,最终目标是将系统OS拉起并运行。
3)整个系统的加载任务都是由BootLoader完成的。
2. kernel启动
在编译完AOSP时会生成boot.img或者boot_debug.img,该镜像就是 Linux 内核和根文件系统,bootloader 会把该镜像装载到内存中,然后 linux 内核会执行整个系统的初始化,完成后装载根文件系统,最后启动 init 进程。
1)当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。
2)在内核完成系统设置后,它首先在系统文件中寻找init.rc,并启动init进程
kernel会去启动init,启动log如下
01-05 23:45:42.396 1 1 I : Run /init as init process
01-05 23:45:42.396 1 1 D with arguments:
01-05 23:45:42.396 1 1 D : /init
01-05 23:45:42.396 1 1 D with environment:
01-05 23:45:42.396 1 1 D : HOME=/
01-05 23:45:42.396 1 1 D : TERM=linux
3. init进程启动
init是被kernel拉起来的。
init进程是Android系统启动后,由内核启动的第一个用户级进程,init的进程号为1.
Android中所有进程都是由init进程创建并运行的。
init启动主要有三大步骤,分别为
1. FirstStageMain(第一阶段):主要挂载基本的文件系统,挂载特定分区等
2. SetupSelinux(Selinux配置阶段)
3. SecondStageMain(第二阶段)
如果抓一份开机的log,会发现有如下log输出。分别对应各个步骤的log,且可以看到pid为1。
01-05 23:45:42.397 1 1 I init : init first stage started!
01-05 23:45:42.812 1 1 I init : Opening SELinux policy
01-05 23:45:42.819 1 1 I init : Loading SELinux policy
01-05 23:45:42.943 1 1 I init : init second stage started!
init相关代码目录为:
/system/core/init
init进程启动后,首先会走它的main方法,首先会设置init进程优先级为最大,再根据携带的参数决定走哪个方法,由于kernel启动的时候没有携带参数,所以argc是1。
代码如下
/system/core/init/main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
//setpriority(int which, int who, int prio);
//prio范围为:-20~20,prio越小,优先级越大。
//which为PRIO_PROCESS时,含义为设置进程号为who的优先级为prio
//设置init进程的优先级为最高优先级
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);
}
}
//由于kernel启动init的时候没有携带任何参数,所以会首先走入此方法
return FirstStageMain(argc, argv);
}
3.1 FirstStageMain
接着讲FirstStageMain函数具体做了什么。
由于执行的步骤太多,下面一步步来分析具体做了什么
3.1.1 init若崩溃会重新启动BootLoader
当init崩溃时,为了防止内核进入panic状态,会重新启动BootLoader。
int FirstStageMain(int argc, char** argv) {
//1
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
1)InstallRebootSignalHandlers
InstallRebootSignalHandlers中主要做了以下几件事:
1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。
2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot
3. 通过syscall向内核发送重启命令
4. 捕获一些信号
void InstallRebootSignalHandlers() {
struct sigaction action;
memset(&action, 0, sizeof(action));
//用于初始化一个自定义信号集,将其所有信号都填充满,也就是将信号集中的所有的
//标志位置为1,使得这个集合包含所有可接受的信号
//这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。
sigfillset(&action.sa_mask);
action.sa_handler = [](int signal) {
//从init派生的进程也会捕获这些信号处理程序,但是我们不希望它们触发重新启动,
//所以我们在这里直接为子进程调用_exit()。
if (getpid() != 1) {
_exit(signal);
}
//调用DoReboot()或LOG(FATAL)不是一个好的选择,因为这是一个信号处理程序。
//RebootSystem使用syscall()
InitFatalReboot(signal);
};
action.sa_flags = SA_RESTART;
//sigaction(int signum, const struct sigaction *act,struct sigaction *oldact)
//参数1:要捕获的信号
//参数2:接收到信号之后对信号进行处理的结构体
//参数3:接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL
sigaction(SIGABRT, &action, nullptr);
sigaction(SIGBUS, &action, nullptr);
sigaction(SIGFPE, &action, nullptr);
sigaction(SIGILL, &action, nullptr);
sigaction(SIGSEGV, &action, nullptr);
#if defined(SIGSTKFLT)
sigaction(SIGSTKFLT, &action, nullptr);
#endif
sigaction(SIGSYS, &action, nullptr);
sigaction(SIGTRAP, &action, nullptr);
}
2)InitFatalReboot
InitFatalReboot做了以下几件事:
1. 判断init是否可以创建子进程,如果不能创建,则直接重启;如果能创建,则sleep 5ms,再重启子进程。
2. 如果是init进程,则获取异常的backtrace。
3. 如果init进程被标记为异常了,则往/proc/sysrq-trigger写c,让系统陷入崩溃,然后直接退出。
echo b > /proc/sysrq-trigger 立即重启
echo o > /proc/sysrq-trigger 立即关机
echo c > /proc/sysrq-trigger 立即让系统崩溃
echo t > /proc/sysrq-trigger 导出线程状态信息
echo u > /proc/sysrq-trigger 立即重新挂载所有的文件系统为只读
4.如果init没有被标记为异常,则重启init进程。
void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
auto pid = fork();//创建子进程,成功0,失败-1
if (pid == -1) {
//不能创建子进程,不用尝试获取backtrace,直接重启
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
} else if (pid == 0) {
// 可以创建子进程,说明当前在子进程上,并且子进程需确保能重启
sleep(5);
//子进程重启
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
}
//尝试获取init的backtrace再关机重启
LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
if (!backtrace->Unwind(0)) {
LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
}
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
LOG(ERROR) << backtrace->FormatFrameData(i);
}
//判断init是否被标记为异常
//异常时:/proc/cmdline中有节点androidboot.init_fatal_panic,并且其值为true
if (init_fatal_panic) {
LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
//往/proc/sysrq-trigger写c
android::base::WriteStringToFile("c", PROC_SYSRQ);
LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
_exit(signal_number);
}
//init重启
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
}
3)RebootSystem
走到这个函数,说明就要发送重启命令了,是通过syscall去发送的。
在用户空间和内核空间之间,通过syscall(系统调用, system call)的中间层来通信,连接用户态和内核态的桥梁。用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作。
RebootSystem如下:
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
if (!IsRebootCapable()) {
// On systems where init does not have the capability of rebooting the
// device, just exit cleanly.
exit(0);
}
switch (cmd) {
case ANDROID_RB_POWEROFF:
reboot(RB_POWER_OFF);
break;
//前面传递的cmd是ANDROID_RB_RESTART2,发送重启命令
//Syscall是连接用户态和内核态的桥梁。
//用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作
case ANDROID_RB_RESTART2:
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
break;
case ANDROID_RB_THERMOFF:
if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
} else {
reboot(RB_POWER_OFF);
}
break;
}
// In normal case, reboot should not return.
PLOG(ERROR) << "reboot call returned";
abort();
}
3.1.2 创建以及挂载文件系统
第二步,主要是创建和挂载文件系统
//设置允许当前进程创建文件或者目录最大可操作的权限
umask(0);
//清楚环境变量
CHECKCALL(clearenv());
//设置环境变量
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
//设置/dev为tmpfs类型,并挂载,且权限为0755,本进程可读可写可执行,本组和其他组只能读写
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));
//设置/dev/pts为devpts类型,并挂载
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
// 读取操作系统的启动参数
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));
//创建kernel log节点
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)));
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
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"))
#undef CHECKCALL
查看机器的mount挂载详情表: adb shell mount
可以看到有很多种文件类型。
$:/ # mount
tmpfs on /dev type tmpfs (rw,seclabel,nosuid,relatime,size=7984680k,nr_inodes=1996170,mode=755)
devpts on /dev/pts type devpts (rw,seclabel,relatime,mode=600,ptmxmode=000)
proc on /proc type proc (rw,relatime,gid=3009,hidepid=invisible)
sysfs on /sys type sysfs (rw,seclabel,relatime)
selinuxfs on /sys/fs/selinux type selinuxfs (rw,relatime)
tmpfs on /mnt type tmpfs (rw,seclabel,nosuid,nodev,noexec,relatime,size=7984680k,nr_inodes=1996170,mode=755,gid=1000)
/dev/block/sda9 on /metadata type ext4 (rw,seclabel,nosuid,nodev,noatime,discard)
/dev/block/dm-2 on / type ext4 (ro,seclabel,nodev,relatime,discard)
/dev/block/dm-3 on /system_ext type ext4 (ro,seclabel,relatime,discard)
/dev/block/dm-1 on /product type ext4 (ro,seclabel,relatime,discard)
/dev/block/dm-4 on /vendor type ext4 (ro,seclabel,relatime,discard)
/dev/block/dm-5 on /vendor_dlkm type ext4 (ro,seclabel,relatime,discard)
/dev/block/dm-0 on /odm type ext4 (ro,seclabel,relatime,discard)
/dev/block/dm-6 on /mnt/scratch type f2fs (rw,sync,lazytime,seclabel,noatime,background_gc=on,nodiscard,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,alloc_mode=reuse,checkpoint_merge,fsync_mode=posix,memory=normal)
overlay on /system type overlay (ro,seclabel,noatime,lowerdir=/system,upperdir=/mnt/scratch/overlay/system/upper,workdir=/mnt/scratch/overlay/system/work,override_creds=off)
overlay on /system_ext type overlay (ro,seclabel,noatime,lowerdir=/system_ext,upperdir=/mnt/scratch/overlay/system_ext/upper,workdir=/mnt/scratch/overlay/system_ext/work,override_creds=off)
overlay on /product type overlay (ro,seclabel,noatime,lowerdir=/product,upperdir=/mnt/scratch/overlay/product/upper,workdir=/mnt/scratch/overlay/product/work,override_creds=off)
overlay on /vendor type overlay (ro,seclabel,noatime,lowerdir=/vendor,upperdir=/mnt/scratch/overlay/vendor/upper,workdir=/mnt/scratch/overlay/vendor/work,override_creds=off)
overlay on /vendor_dlkm type overlay (ro,seclabel,noatime,lowerdir=/vendor_dlkm,upperdir=/mnt/scratch/overlay/vendor_dlkm/upper,workdir=/mnt/scratch/overlay/vendor_dlkm/work,override_creds=off)
overlay on /odm type overlay (ro,seclabel,noatime,lowerdir=/odm,upperdir=/mnt/scratch/overlay/odm/upper,workdir=/mnt/scratch/overlay/odm/work,override_creds=off)
...
...
讲一下这些都代表什么意思
tmpfs on /dev type tmpfs (rw,seclabel,nosuid,relatime,size=7984680k,nr_inodes=1996170,mode=755)
tmpfs 挂载点
tmpfs:文件系统类型
/dev:设备文件名
ro: readonly,只读挂载;
rw: read and write, 读写挂载nosuid 关闭set-user-identifier(设置用户ID)与set-group-identifer(设置组ID)设置位。
mode=755:挂载权限为0755,代表rwxr-xr-x.
权限说明:
权限: | 0 | 7 | 5 | 5 |
---|---|---|---|---|
含义 | 当前用户 | 组用户group | 其他用户 | |
可拥有的权限 | rwx | r-x | r-x |
一般赋予目录0755权限,文件0644权限。
每个组group的权限范围为0-7,含义如下
ls -l结果 | 值 | 二进制 | 含义 |
---|---|---|---|
--- | 0 | 000 | no excute , no write ,no read |
--x | 1 | 001 | excute, (no write, no read) |
-w- | 2 | 010 | write |
-wx | 3 | 011 | write, excute |
r-- | 4 | 100 | read |
r-x | 5 | 101 | read, excute |
rw- | 6 | 110 | read, write |
rwx | 7 | 111 | read, write , excute |
查看设备节点内存,可以看到system,vendor,product都挂载到了哪里。
adb shell df -h
$:/ # df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 7.6G 1.6M 7.6G 1% /dev
tmpfs 7.6G 0 7.6G 0% /mnt
/dev/block/sda9 11M 188K 11M 2% /metadata
/dev/block/dm-2 914M 912M 2.7M 100% /
/dev/block/dm-6 1.9G 230M 1.7G 12% /mnt/scratch
overlay 1.9G 230M 1.7G 12% /system
overlay 1.9G 230M 1.7G 12% /system_ext
overlay 1.9G 230M 1.7G 12% /product
overlay 1.9G 230M 1.7G 12% /vendor
overlay 1.9G 230M 1.7G 12% /vendor_dlkm
overlay 1.9G 230M 1.7G 12% /odm
tmpfs 7.6G 8.0K 7.6G 1% /apex
tmpfs 7.6G 576K 7.6G 1% /linkerconfig
/dev/block/sda2 27M 1.1M 26M 5% /mnt/vendor/persist
/dev/block/sde6 300M 54M 246M 19% /vendor/firmware_mnt
/dev/block/sde11 59M 39M 20M 67% /vendor/dsp
/dev/block/sde7 64M 2.9M 61M 5% /vendor/bt_firmware
/dev/block/sde26 30M 0 30M 0% /mnt/vendor/qmcs
/dev/block/dm-7 45G 4.7G 40G 11% /data
tmpfs 7.6G 0 7.6G 0% /data_mirror
3.1.3 kernel log初始化
SetStdioToDevNull(argv);
//可以输出kernel log了,前面已经创建了/dev/dmsg
InitKernelLogging(argv);
if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
LOG(ERROR) << error_string << " " << strerror(error_errno);
}
LOG(FATAL) << "Init encountered errors starting first stage, aborting";
}
//此时log系统已经可以用了
LOG(INFO) << "init first stage started!";
3.1.4 加载/lib/moudles/下的ko文件, 打开串口log
这一部分的代码主要功能如下:
1)打开根目录/
2)加载/lib/moudles/下的ko文件
01-05 23:45:42.399 1 1 I init : Loading module /lib/modules/qcom_hwspinlock.ko with args ''
01-05 23:45:42.401 1 1 I init : Loaded kernel module /lib/modules/qcom_hwspinlock.ko
01-05 23:45:42.401 1 1 I init : Loading module /lib/modules/smem.ko with args ''
01-05 23:45:42.403 1 1 I init : Loaded kernel module /lib/modules/smem.ko
01-05 23:45:42.403 1 1 I init : Loading module /lib/modules/minidump.ko with args ''
01-05 23:45:42.408 1 1 I init : Loaded kernel module /lib/modules/minidump.ko
01-05 23:45:42.408 1 1 I init : Loading module /lib/modules/qcom-scm.ko with args ''
01-05 23:45:42.416 1 1 I init : Loaded kernel module /lib/modules/qcom-scm.ko
01-05 23:45:42.416 1 1 I init : Loading module /lib/modules/qcom_wdt_core.ko with args ''
3)打开串口log
//打开根目录/
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}
struct stat old_root_info;
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
//加载/lib/modules下的ko文件
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
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";
}
bool created_devices = false;
//根据ALLOW_FIRST_STAGE_CONSOLE(want_console)决定是否打开串口log
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
if (!IsRecoveryMode()) {
created_devices = DoCreateDevices();
if (!created_devices){
LOG(ERROR) << "Failed to create device nodes early";
}
}
StartConsole(cmdline);
}
3.1.5 拷贝ramdisk prop文件,创建new ramdisk, 删除old ramdisk
这一部分的代码主要功能如下:
1)打开/system/etc/ramdisk/build.prop文件,如果可以打开,则创建/second_stage_resources/system/etc/ramdisk/build.prop文件,并将/system/etc/ramdisk/build.prop拷贝到second_stage_resources/system/etc/ramdisk/build.prop下。
2)打开/force_debugable, 如果存在“/force_debugable”,则第二阶段init将使用userdebug sepolicy并加载adb_debug.prop以允许adb root
3)创建/first_stage_ramdisk并挂载,然后将根目录切换到/first_stage_ramdisk
4)挂载 system、vendor 、product等系统分区
5)free old ramdisk
//打开/system/etc/ramdisk/build.prop文件
if (access(kBootImageRamdiskProp, F_OK) == 0) {
//生成/second_stage_resources/system/etc/ramdisk/build.prop
//constexpr const char kSecondStageRes[] = "/second_stage_resources";
//constexpr const char kBootImageRamdiskProp[] = "/system/etc/ramdisk/build.prop";
//inline std::string GetRamdiskPropForSecondStage() {
// return std::string(kSecondStageRes) + kBootImageRamdiskProp;
//}
std::string dest = GetRamdiskPropForSecondStage();
std::string dir = android::base::Dirname(dest);
std::error_code ec;
if (!fs::create_directories(dir, ec) && !!ec) {
LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
}
if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {
LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "
<< ec.message();
}
LOG(INFO) << "Copied ramdisk prop to " << dest;
}
// If "/force_debuggable" is present, the second-stage init will use a userdebug
// sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
// 如果存在“/force_debugable”,则第二阶段init将使用userdebug sepolicy并加载adb_debug.prop以允许adb root
// /userdebug_plat_sepolicy.cil属于selinux策略里的规则
// 如果设备unlocked(解锁了),则会修改selinux规则,放大用户权限
if (access("/force_debuggable", F_OK) == 0) {
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
!fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
LOG(ERROR) << "Failed to setup debug ramdisk";
} else {
// setenv for second-stage init to read above kDebugRamdisk* files.
//在second init阶段可以用到
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
}
if (ForceNormalBoot(cmdline, bootconfig)) {
mkdir("/first_stage_ramdisk", 0755);
//挂载first_stage_ramdisk到first_stage_ramdisk
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
//将根目录切换到/first_stage_ramdisk
SwitchRoot("/first_stage_ramdisk");
}
//挂载 system、vendor 、product等系统分区
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
struct stat new_root_info;
//new_root_info是/first_stage_ramdisk
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
//old_root_info是/
if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}
SetInitAvbVersionInRecovery();
//设置环境变量
setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
3.1.6 重新执行init进程并携带参数selinux_setup
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);
//重新执行/system/bin/init,并携带了参数selinux_setup
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
后文接着讲后续流程