参考:
https://www.it610.com/article/1304931662924124160.htm
https://blog.csdn.net/pepsimaxin/article/details/107284563
https://juejin.cn/post/6844903946151002125
基于 LineageOS 18.1,Android 11。
简述:
1)按下电源系统启动
当电源按下时引导芯片代码开始从预定义的地方(固化在 ROM)开始执行,加载引导程序 Bootloader 到 RAM,然后执行。
2)引导程序 Bootloader
引导程序是在 Android 操作系统开始运行前的一个小程序,它的主要作用是把系统 OS 拉起来并运行。
3)linux内核启动
内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找 "init" 文件 ,然后启动 root 进程或者系统的第一个进程。
4)启动 init 进程
(本文内容为其中一小部分)
5)启动 Zygote 进程
创建 JavaVM 并为 JavaVM 注册 JNI,创建服务端 Socket,启动 SystemServer 进程。
6)启动 SystemServer 进程
启动 Binder 线程池和 SystemServiceManager,并且启动各种系统服务。
7)启动 Launcher
SystemServer 启动的 ActivityManagerService 会负责启动 Launcher,Launcher 启动后会将已安装应用的快捷图标显示到界面上。
系统入口main
system/core/init/main.cpp
在这里把启动过程中的操作大致分了几部分,分别执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#include "builtins.h" #include "first_stage_init.h" #include "init.h" #include "selinux.h" #include "subcontext.h" #include "ueventd.h" #include <android-base/logging.h> #if __has_feature(address_sanitizer) #include <sanitizer/asan_interface.h> #endif #if __has_feature(address_sanitizer) // Load asan.options if it exists since these are not yet in the environment. // Always ensure detect_container_overflow=0 as there are false positives with this check. // Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds. extern "C" const char* __asan_default_options() { return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1"; } __attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void __sanitizer_report_error_summary(const char* summary) { LOG(ERROR) << "Init (error summary): " << summary; } __attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void AsanReportCallback(const char* str) { LOG(ERROR) << "Init: " << str; } #endif using namespace android::init; // 入口 int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #endif // Boost prio which will be restored later setpriority(PRIO_PROCESS, 0, -20); // PRIO_PROCESS:表示第2个参数为进程识别码。0:进程id。-20:最高优先级(最低20 —— 最高-20) if (!strcmp(basename(argv[0]), "ueventd")) { // 根据传入的参数,启动ueventd // system/core/init/ueventd.cpp:269:int ueventd_main(int argc, char** argv) { return ueventd_main(argc, argv); // 创建设备节点、权限设定等 } if (argc > 1) { // 其它传入的参数 if (!strcmp(argv[1], "subcontext")) { // 启动subcontext // system/core/base/logging.cpp:342:void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { android::base::InitLogging(argv, &android::base::KernelLogger); // 初始化日志系统 // system/core/init/builtins.cpp:1338:const BuiltinFunctionMap& GetBuiltinFunctionMap() { // system/core/init/builtins.h:33:struct BuiltinFunctionMapValue { // system/core/init/builtins.h:38:using BuiltinFunctionMap = KeywordMap<BuiltinFunctionMapValue>; // system/core/init/builtins.h:40:const BuiltinFunctionMap& GetBuiltinFunctionMap(); // 函数跳转表 const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); // system/core/init/subcontext.cpp:175:int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map) { return SubcontextMain(argc, argv, &function_map); } if (!strcmp(argv[1], "selinux_setup")) { //【重点】启动selinux安全策略 ---> 进一步init第2阶段 // system/core/init/selinux.cpp:672:int SetupSelinux(char** argv) { return SetupSelinux(argv); } if (!strcmp(argv[1], "second_stage")) { //【重点】一步到位启动 ---> init第2阶段 // system/core/init/init.cpp:689:int SecondStageMain(int argc, char** argv) { return SecondStageMain(argc, argv); // Android阶段,解析init.rc、启动若干服务、创建epoll、等 } } // system/core/init/first_stage_init.cpp:174:int FirstStageMain(int argc, char** argv) { return FirstStageMain(argc, argv); //【重点】启动 init第1阶段,Linux阶段,挂载文件系统 ---> 进一步init第2阶段 } |
FirstStageMain
main --> FirstStageMain --> SetupSelinux --> SecondStageMain
system/core/init/first_stage_main.cpp =>
system/core/init/first_stage_init.cpp
通常称这是init的第一阶段,主要是Linux启动加载的基础性操作。包括
●处理init进程挂掉的情况
●创建并挂载相关系统文件
●设置用户组,
●重定向输入输出/内核 Log 系统
●挂载特定分区
●通过init进入SetupSelinux
Linux中的几种文件系统:
▶ tmpfs:虚拟内存文件系统,它会将所有的文件存储在虚拟内存中。当tmpfs文件系统卸载或关机或系统断电,其中的内容将清除不可恢复。tmpfs既可以使用 RAM,也可以使用交换分区,会根据你的实际需要而改变大小。速度很快,即使是使用交换分区。
▶ devpts:为伪终端提供了一个标准接口,它的标准挂接点是 /dev/pts。只要 pty 的主复合设备 /dev/ptmx 被打开,就会在 /dev/pts 下动态的创建一个新的 pty 设备文件。
▶ proc:重要的虚拟文件系统,可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。
▶ sysfs:与 proc 类似,也是不占有任何磁盘空间的虚拟文件系统。通常被挂接在 /sys 目录下。sysfs 文件系统是 Linux2.6 内核引入的,它把连接在系统上的设备和总线组织成一个分级的文件,使得它们可以在用户空间存取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
#include "first_stage_init.h" #include <dirent.h> #include <fcntl.h> #include <paths.h> #include <stdlib.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/sysmacros.h> #include <sys/types.h> #include <sys/utsname.h> #include <unistd.h> #include <filesystem> #include <string> #include <vector> #include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> // system/core/base/include/android-base/logging.h #include <modprobe/modprobe.h> #include <private/android_filesystem_config.h> #include "debug_ramdisk.h" #include "first_stage_console.h" #include "first_stage_mount.h" #include "reboot_utils.h" #include "switch_root.h" #include "util.h" using android::base::boot_clock; using namespace std::literals; namespace fs = std::filesystem; namespace android { namespace init { namespace { void FreeRamdisk(DIR* dir, dev_t dev) { int dfd = dirfd(dir); dirent* de; while ((de = readdir(dir)) != nullptr) { if (de->d_name == "."s || de->d_name == ".."s) { continue; } bool is_dir = false; if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) { struct stat info; if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) { continue; } if (info.st_dev != dev) { continue; } if (S_ISDIR(info.st_mode)) { is_dir = true; auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC); if (fd >= 0) { auto subdir = std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir}; if (subdir) { FreeRamdisk(subdir.get(), dev); } else { close(fd); } } } } unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0); } } // 从指令中查找特定的一项 bool ForceNormalBoot(const std::string& cmdline) { return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos; } } // namespace std::string GetModuleLoadList(bool recovery, const std::string& dir_path) { auto module_load_file = "modules.load"; if (recovery) { struct stat fileStat; std::string recovery_load_path = dir_path + "/modules.load.recovery"; if (!stat(recovery_load_path.c_str(), &fileStat)) { module_load_file = "modules.load.recovery"; } } return module_load_file; } // 打开串口 #define MODULE_BASE_DIR "/lib/modules" bool LoadKernelModules(bool recovery, bool want_console) { struct utsname uts; if (uname(&uts)) { LOG(FATAL) << "Failed to get kernel version."; } int major, minor; if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) { LOG(FATAL) << "Failed to parse kernel version " << uts.release; } std::unique_ptr<DIR, decltype(&closedir)> base_dir(opendir(MODULE_BASE_DIR), closedir); if (!base_dir) { LOG(INFO) << "Unable to open /lib/modules, skipping module loading."; return true; } dirent* entry; std::vector<std::string> module_dirs; while ((entry = readdir(base_dir.get()))) { if (entry->d_type != DT_DIR) { continue; } int dir_major, dir_minor; if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major || dir_minor != minor) { continue; } module_dirs.emplace_back(entry->d_name); } // Sort the directories so they are iterated over during module loading in a consistent order. // Alphabetical sorting is fine here because the kernel version at the beginning of the directory name must match the current kernel version, // so the sort only applies to a label that follows the kernel version, // for example /lib/modules/5.4 vs. /lib/modules/5.4-gki. std::sort(module_dirs.begin(), module_dirs.end()); for (const auto& module_dir : module_dirs) { std::string dir_path = MODULE_BASE_DIR "/"; dir_path.append(module_dir); Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path)); bool retval = m.LoadListedModules(!want_console); int modules_loaded = m.GetModuleCount(); if (modules_loaded > 0) { return retval; } } Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR)); bool retval = m.LoadListedModules(!want_console); int modules_loaded = m.GetModuleCount(); if (modules_loaded > 0) { return retval; } return true; } // 入口 int FirstStageMain(int argc, char** argv) { // REBOOT_BOOTLOADER_ON_PANIC 定义: // ./system/core/init/Android.bp:98: "-DREBOOT_BOOTLOADER_ON_PANIC=0", // ./system/core/init/Android.bp:113: "-UREBOOT_BOOTLOADER_ON_PANIC", // ./system/core/init/Android.bp:114: "-DREBOOT_BOOTLOADER_ON_PANIC=1", // ./system/core/init/Android.mk:14: -DREBOOT_BOOTLOADER_ON_PANIC=1 \/ // ./system/core/init/Android.mk:22: -DREBOOT_BOOTLOADER_ON_PANIC=0 \/ if (REBOOT_BOOTLOADER_ON_PANIC) { // 在顶层 init 模块的 mk 文件中定义 // system/core/init/reboot_utils.cpp:150:void InstallRebootSignalHandlers() { InstallRebootSignalHandlers(); // 当init挂掉,重启bootloader } 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)); // 设置环境变量,_PATH_DEFPATH在bionic/libc/include/paths.h中有定义,主要是shell 启动bin文件的查找路径集合 // 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")); // 使用tmpfs文件系统挂载dev目录 CHECKCALL(mkdir("/dev/pts", 0755)); // 创建dev/pts目录 :是远程登陆(telnet,ssh等)后创建的控制台设备文件所在的目录 CHECKCALL(mkdir("/dev/socket", 0755)); // 创建dev/socket目录,init会为一些native进程创建socket,会在该目录下生产对应的socket节点 CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL)); // 使用devpts文件系统挂载/dev/pts #define MAKE_STR(x) __STRING(x) CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); // 使用proc文件系统挂载/proc #undef MAKE_STR // Don't expose the raw commandline to unprivileged processes. CHECKCALL(chmod("/proc/cmdline", 0440)); // 限制目录权限 /proc/cmdline中保存bootloader 启动linux kernel 时 的参数 std::string cmdline; android::base::ReadFileToString("/proc/cmdline", &cmdline); // 把参数读取到‘cmdline’字符串里,下文用到 gid_t groups[] = {AID_READPROC}; CHECKCALL(setgroups(arraysize(groups), groups)); // 设置用户组 CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); // 挂载sysfs文件系统在sys目录,用来访问内核信息 CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL)); // 挂载文件系统selinuxfs到目录/sys/fs/selinux,其中是selinux相关的目录和节点 CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11))); // 创建/dev/kmsg设备节点文件,用于存到kenel log日志 if constexpr (WORLD_WRITABLE_KMSG) { CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11))); } // 节点/dev/random和/dev/urandom 是Linux系统中提供的随机伪设备,这两个设备的任务,是提供永不为空的随机字节数据流。 // 很多解密程序与安全应用程序(如SSH Keys,SSL Keys等)需要它们提供的随机数据流。 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. // 这是日志包装器所需要的,它在ueventd运行之前被调用。 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/ // 挂载tmpfs文件系统到mnt目录,在第1阶段init中完成,以便第1阶段还可以挂载 /mnt/{vendor,product}/ 的子目录:光驱,usb设备 // 无须在第1阶段挂载的,将在rc文件中完成。 // 由vold管理的 设备装载临时区 的挂载,请参阅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. // /mnt/ 用于挂载特定供应商的分区,但不能作为供应商分区的一部分,因为它们是以读写方式安装的。 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. // /mnt/product 用于挂载特定产品的分区,但不能作为产品分区的一部分,因为它们是以读写方式装载的。 CHECKCALL(mkdir("/mnt/product", 0755)); // /debug_ramdisk is used to preserve additional files from the debug ramdisk // /debug_ramdisk 用于保留 debug ramdisk 中的其它文件 CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=0")); #undef CHECKCALL // system/core/init/util.cpp:701:void SetStdioToDevNull(char** argv) { SetStdioToDevNull(argv); // 把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null" // system/core/init/util.cpp:716:void InitKernelLogging(char** argv) { // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually talk to the outside world... // 现在tmpfs文件系统已经挂载到 /dev 上,并且有了 /dev/kmsg,就可以和外面到世界沟通啦 InitKernelLogging(argv); // 初始化 kernel Log 系统,定向到dev/kmsg if (!errors.empty()) { for (const auto& [error_string, error_errno] : errors) { LOG(ERROR) << error_string << " " << strerror(error_errno); // 可以使用log啦。 } LOG(FATAL) << "Init encountered errors starting first stage, aborting"; } LOG(INFO) << "init first stage started!"; 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) : 0; // system/core/init/first_stage_console.cpp:91:int FirstStageConsole(const std::string& cmdline) { // 根据配置决定是否打开串口 if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) { if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) << "Failed to load kernel modules, starting console"; } else { LOG(FATAL) << "Failed to load kernel modules"; } } if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) { // system/core/init/first_stage_console.cpp:51:void StartConsole() { StartConsole(); } // 对于将恢复用作 ramdisk 的设备,第一阶段 init 位于恢复 ramdisk 中的 /init。这些设备首先将根切换到 first_stage_ramdisk,以便从环境中移除恢复组件, // 然后执行与具有 boot-ramdisk 的设备一样的操作(即,将 system.img 作为 /system 进行装载,切换根以将该装载移动到 /,然后在装载完成后释放 ramdisk 内容)。 // 如果内核命令行中存在 androidboot.force_normal_boot=1,则设备会正常启动(启动到 Android)而不是启动到恢复模式。 // 介绍:https://source.android.google.cn/devices/bootloader/system-as-root?hl=zh-cn#ramdisk if (ForceNormalBoot(cmdline)) { mkdir("/first_stage_ramdisk", 0755); // 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) { LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself"; } // system/core/init/switch_root.cpp:72:void SwitchRoot(const std::string& new_root) { SwitchRoot("/first_stage_ramdisk"); } // If this file 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_debuggable文件存在,第2阶段init将使用userdebug规则,并加载 adb_debug.prop 以支持adb root指令(userdebug sepolicy) 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. setenv("INIT_FORCE_DEBUGGABLE", "true", 1); } } // system/core/init/first_stage_mount.cpp:800:bool DoFirstStageMount() { // init的第1阶段(即初始化 SElinux 之前)装载 /system、/vendor 或 /odm,这个主要是因为 打开了Treble架构的设备上, // 为了确保 init能及时导入SELinux的配置文件(contexts/*.te),需要尽快的将system/vendor等分区挂载上。 // 这句话要对比AndroidN来理解:在AndroidN上,selinux的配置文件存放在boot.img中,在内核初始化过程中,boot.img中的文件已经挂载到rootfs了, // 相应的,配置文件也就可以从rootfs读取了。而AndroidO开始,selinux配置文件放到了vendor/system分区, // 如果仍然按照do_mount_all阶段来挂载这两个分区,selinux来不及做初始化。 if (!DoFirstStageMount()) { // 检查、读取 DT(device-tree) 中记录的分区挂载信息。如该DT的fstab配置支持,则直接跳过;不支持则进行挂载 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(); } // 根目录发生变化,则释放ramdisk if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) { FreeRamdisk(old_root_dir.get(), old_root_info.st_dev); } // system/core/init/first_stage_mount.cpp:815:void SetInitAvbVersionInRecovery() { // 初始化安全框架 Android Verified Boot,用于防止系统文件本身被篡改、防止系统回滚,以免回滚系统利用以前的漏洞。包括Secure Boot, verfying boot 和 dm-verity, // 原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。其中认证的范围涵盖:bootloader,boot.img,system.img。 // 此处是在recovery模式下初始化avb的版本,不是recovery模式直接跳过 SetInitAvbVersionInRecovery(); setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1); const char* path = "/system/bin/init"; // init程序的二进制文件目录 const char* args[] = {path, "selinux_setup", nullptr}; // 指令:/system/bin/init selinux_setup,参数 selinux_steup 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));// 【重点】进入SetupSelinux。只有发生错误时才返回 // execv() only returns if an error happened, in which case we panic and never fall through this conditional. // init发生错误时才会执行到这里,但无法获取发生错误的原由 PLOG(FATAL) << "execv(\"" << path << "\") failed"; return 1; } } // namespace init } // namespace android |
- end
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/4021.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设