int FirstStageMain( int argc, char ** argv) {
//init crash时重启引导加载程序
//这个函数主要作用将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,一旦监听到这些信号即执行重启系统
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
std::vector<std::pair<std::string, int >> errors;
#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));
//在RAM内存上获取基本的文件系统,剩余的被rc文件所用
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
//非特权应用不能使用Android cmdline
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)));
//这对于日志包装器是必需的,它在ueventd运行之前被调用
CHECKCALL(mknod( "/dev/ptmx" , S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod( "/dev/null" , S_IFCHR | 0666, makedev(1, 3)));
//在第一阶段挂在tmpfs、mnt/vendor、mount/product分区。其他的分区不需要在第一阶段加载,
//只需要在第二阶段通过rc文件解析来加载
CHECKCALL(mount( "tmpfs" , "/mnt" , "tmpfs" , MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000" ));
//创建可供读写的vendor目录
CHECKCALL(mkdir( "/mnt/vendor" , 0755));
CHECKCALL(mkdir( "/mnt/product" , 0755));
// 挂载APEX,这在Android 10.0中特殊引入,用来解决碎片化问题,类似一种组件方式,对Treble的增强,
// 不写谷歌特殊更新不需要完整升级整个系统版本,只需要像升级APK一样,进行APEX组件升级
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
//把标准输入、标准输出和标准错误重定向到空设备文件“/dev/null”
SetStdioToDevNull(argv);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
#ifdef MTK_LOG
#ifndef MTK_LOG_DISABLERATELIMIT
if (cmdline.find( "init.mtklogdrl=1" ) != std::string::npos)
SetMTKLOGDISABLERATELIMIT();
#else
SetMTKLOGDISABLERATELIMIT();
#endif // MTK_LOG_DISABLERATELIMIT
if (GetMTKLOGDISABLERATELIMIT())
InitKernelLogging_split(argv);
else
InitKernelLogging(argv);
#else
//在/dev目录下挂载好tmpfs以及kmsg
//这样就可以初始化/kernel Log系统,供用户打印log
InitKernelLogging(argv);
#endif
......
/*
初始化一些必须的分区
主要作用是去解析/proc/device-tree/firmware/android/fstab
然后得到“/system”,“/vendor”,“/odm”三个目录的挂载信息
*/
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "Failed to mount required partitions early ..." ;
}
struct stat new_root_info;
if (stat( "/" , &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk" ;
old_root_dir.reset();
}
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);
//启动init进程,传入参数selinux_steup
//执行命令:/system/bin/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);
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;
}
|