我的Android进阶修炼:安卓启动流程之init(1)
文章目录
- 我的Android进阶修炼:安卓启动流程之init(1)
- 一、前言
- 二、init进程简介
- 1.文件位置
- 2.主要功能
- 三、init进程源码分析
- 3.1 main() 源码注解
- 3.1.1 参考:setpriority
- 3.1.2 参考:ueventd
- 3.1.3 参考:InitLogging
- 3.1.4 参考:SubcontextMain
- 3.1.5 参考:selinux_setup
- 3.2 FirstStageMain() 源码注解
- 3.2.1 参考:REBOOT_BOOTLOADER_ON_PANIC
- 3.2.2 参考:umask(0)
- 3.2.3 参考:_PATH_DEFPATH
- 3.2.4 参考:/proc/cmdline
- Android 12 的 cmdline
- Android 11 的 cmdline
- 3.2.5 参考:/proc/bootconfig
- 3.2.6 参考:SetStdioToDevNull
- (1) SetStdioToDevNull
- (2) dup2
- 四、篇尾
我的Android进阶修炼:安卓启动流程之init(1)
一、前言
希望深入研究Android系统,却也一直找不到合适的方向,所以仿照大神做法,同样,也以1号init进程为主轴,开始修炼吧!
- 基于AOSP Android 9.0:android-9.0.0_r60
- 调试平台:模拟器
二、init进程简介
1.文件位置
Main.cpp (system\core\init) 2594 2022/5/12
2.主要功能
- 是用户空间的1号进程,所有其他进程的父进程
- 解析init.rc文件,按脚本创建、挂载各种目录,启动各种服务
三、init进程源码分析
3.1 main() 源码注解
- system\core\init\Main.cpp
init\Main.cpp编译后的文件名为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// 参考:3.1.1// 设置当前进程的优先级为 -20,了解Android OOM机制的同学应该大致知道,优先级为负值是很高的优先级,基本不会被OOM killer(LMK)杀死。setpriority(PRIO_PROCESS, 0, -20);// 参考:3.1.2// init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd if (!strcmp(basename(argv[0]), "ueventd")) {return ueventd_main(argc, argv);}//如果参数大于1个,即至少2个及以上,则执行下面if块的代码//argc 大于1,根据上文提要,至少有2种情况:1. ./init subcontext 2. ./ueventd subcontextif (argc > 1) {if (!strcmp(argv[1], "subcontext")) {// 参考:3.1.3// 初始化日志系统android::base::InitLogging(argv, &android::base::KernelLogger);const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();// 参考:3.1.4// 跳转到SubcontextMainreturn SubcontextMain(argc, argv, &function_map);}// 参考:3.1.5// 跳转到SetupSelinuxif (!strcmp(argv[1], "selinux_setup")) {return SetupSelinux(argv);}// 参考:3.3// 本文重点:SecondStageMain,进入到init的第二阶段if (!strcmp(argv[1], "second_stage")) {return SecondStageMain(argc, argv);}}// 参考:3.2// 本文重点:FirstStageMain,进入到init的第一阶段 return FirstStageMain(argc, argv);
}
3.1.1 参考:setpriority
#define PRIO_PROCESS 0 //进程
#define PRIO_PGRP 1 //进程组
#define PRIO_USER 2 //用户进程
/**
*Linux setpriority系统调用用于设置进程,进程组,用户进程的优先级,修改进程的nice值, nice值越小,进程的优先级越高。
*
*当which为PRIO_PROCESS时,如果参数who为0,则设置当前进程的进程优先级;如果参数who不为0,则设置进程号为who的进程的优先级。
*/
long setpriority(int which,int who,int niceval)
3.1.2 参考:ueventd
- ueventd的主要工作,是通过两种方式创建设备节点文件:1.冷插拔(例如各板载设备);2.热插拔(如U盘)
// init\Main.cpp编译后的文件名为init,而ueventd是指向init的一个软连接。// 当执行软连接./ueventd 的时候,实际执行的是init文件,而从大学C语言学习可知,argv[0]即所执行文件的文件名:ueventd。// 这是个非常巧妙的写法,当检测到执行的是 ./ueventd 的时候,即跳转到 ueventd_main ()的实现中if (!strcmp(basename(argv[0]), "ueventd")) {return ueventd_main(argc, argv);}
3.1.3 参考:InitLogging
// Configure logging based on ANDROID_LOG_TAGS environment variable.
// We need to parse a string that looks like
//
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
//
// The tag (or '*' for the global level) comes first, followed by a colon and a
// letter indicating the minimum priority level we're expected to log. This can
// be used to reveal or conceal logs with specific tags.
#ifdef __ANDROID__
#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
#else
#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
#endif
void InitLogging(char* argv[],LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,AbortFunction&& aborter = DefaultAborter);
#undef INIT_LOGGING_DEFAULT_LOGGER
3.1.4 参考:SubcontextMain
return SubcontextMain(argc, argv, &function_map);
3.1.5 参考:selinux_setup
if (!strcmp(argv[1], "selinux_setup")) {return SetupSelinux(argv);
}
3.2 FirstStageMain() 源码注解
- 字面意思,一目了然,此为init的第一阶段,那啥是第一阶段该干的活呢?
- init进程第一阶段做的主要工作是挂载所需分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略
int FirstStageMain(int argc, char** argv) {// 参考: 3.2.1// init崩溃时候重启系统:只有userdebug 和 eng 版本的固件中, REBOOT_BOOTLOADER_ON_PANIC才会等于1if (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.// 参考: 3.2.2 // 权限掩码清0,创建文件(含目录等特殊文件)时,将使用默认权限umask(0);// 清除环境变量设定CHECKCALL(clearenv());// 参考:3.2.3 设置环境变量PATHCHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));// 关键部分:从initramdisk中,获取基础的文件系统配置,然后让rc文件解决剩下的问题 // 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.// 挂载一个基于内存的分区,挂载目录为 /dev, 并开始在/dev下创建一系列文件,包括设备节点,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));// 挂载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// 参考:3.2.4// 读取内核的配置参数// 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.// 参考:3.2.5// 读取Android 用户空间的配置参数chmod("/proc/bootconfig", 0440);std::string bootconfig;android::base::ReadFileToString("/proc/bootconfig", &bootconfig);// 将当前进程添加到 AID_READPROC 进程组,从而拥有读取进程文件系统的权限gid_t groups[] = {AID_READPROC};CHECKCALL(setgroups(arraysize(groups), groups));// 下面继续挂载所需的fs,创建所需的节点和目录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 ramdiskCHECKCALL(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 initCHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=0"))
#undef CHECKCALL// 参考:3.2.6// 将标准输入、输出、错误重定向到/dev/null SetStdioToDevNull(argv);// 从此处开始,就可以看到打印信息了// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually// talk to the outside world...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(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, bootconfig) : 0;boot_clock::time_point module_start_time = boot_clock::now();int module_count = 0;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;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);}if (access(kBootImageRamdiskProp, F_OK) == 0) {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.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);}}if (ForceNormalBoot(cmdline, bootconfig)) {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";}SwitchRoot("/first_stage_ramdisk");}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);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);// 以selinux_setup为参数,启动1号进程// 即:/system/bin/init selinux_setupexecv(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;
}
3.2.1 参考:REBOOT_BOOTLOADER_ON_PANIC
通过查找字符串,可知REBOOT_BOOTLOADER_ON_PANIC 在 init 的根目录的MK 文件中定义。
在编译为:userdebug 和 eng 版本的固件中,不会打开该选项。
# ./system/core/init/Android.mk:14
#ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
init_options += \-DALLOW_FIRST_STAGE_CONSOLE=1 \-DALLOW_LOCAL_PROP_OVERRIDE=1 \-DALLOW_PERMISSIVE_SELINUX=1 \-DREBOOT_BOOTLOADER_ON_PANIC=1 \-DWORLD_WRITABLE_KMSG=1 \-DDUMP_ON_UMOUNT_FAILURE=1
else
init_options += \-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
endif
- 主要作用是:当 init 进程崩溃时,重启 bootloader,算是为了方便调试吧。
- install_reboot_signal_handlers 函数将各种信号量,如 SIGABRT、SIGBUS 等的行为设置为 SA_RESTART,一旦监听到这些信号即执行重启系统。
if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();
}
3.2.2 参考:umask(0)
- 作用:umask设定创建文件时候的权限掩码;
- 定义函数: mode_t umask(mode_t mask);
- 函数说明: 例如,在建立文件时默认文件权限为0666,通常umask值默认为 022,则该文件的真正权限则为0666&~022=0644,也就是rw-r–r–。
// Clear the umask.
// 创建文件时使用默认权限,不再额外设限
umask(0);
3.2.3 参考:_PATH_DEFPATH
- _PATH_DEFPATH 的定义在 Paths.h (bionic\libc\include) 2525 2022/5/16
- 看到下面的定义,是不是开始熟悉init在干什么事情了 :)
/** Default shell search path. */
#define _PATH_DEFPATH "/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
3.2.4 参考:/proc/cmdline
- 存放了kernel的启动参数,是由bootloader启动kernel时传入的
- 从 Android 12开始,这里面只存放和kernel相关的参数,和android专用的上层环境相关的部分被挪移到新的变量/proc/bootconfig中
- 对比示例:下面分别使用andorid 12和android 11的模拟器,可见andorid 12的内核启动参数已经减少了很多
Android 12 的 cmdline
emulator64_x86_64_arm64:/ # cat /proc/cmdline
stack_depot_disable=on cgroup_disable=pressure cgroup.memory=nokmem no_timer_check clocksource=pit console=0 cma=288M@0-4G ndns=4 loop.max_part=7 ramoops.mem_address=0xff018000 ramoops.mem_size=0x10000 memmap=0x10000$0xff018000 prin
tk.devkmsg=on bootconfig ndns=4 mac80211_hwsim.radios=0
emulator64_x86_64_arm64:/ #
Android 11 的 cmdline
- 可见如官方原文描述,之前的版本中,在内核参数中混入了许多用户空间参数,例如 androidboot.vbmeta.size
generic_x86:/ # cat /proc/cmdline
no_timer_check clocksource=pit console=0 cma=288M@0-4G ndns=4 mac80211_hwsim.channels=2 loop.max_part=7 ramoops.mem_address=0xff018000 ramoops.mem_size=0x10000 memmap=0x10000$0xff018000 printk.devkmsg=on qemu=1 androidboot.hardware=ranchu androidboot.serialno=EMULATOR31X2X8X0 qemu.gles=1 qemu.settings.system.screen_off_timeout=2147483647 qemu.encrypt=1 qemu.vsync=60 qemu.gltransport=pipe qemu.gltransport.drawFlushInterval=800 qemu.opengles.version=131072 qemu.dalvik.vm.heapsize=512m qemu.camera_protocol_ver=1 qemu.camera_hq_edge_processing=0 androidboot.vbmeta.size=6144 androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.digest=ea5843921b6671f2d851ebf15e7b55f71e73fc36967778bca3be3e3cf4e15f28 androidboot.boot_devices=pci0000:00/0000:00:03.0 qemu.wifi=1 qemu.hwcodec.avcdec=2 qemu.hwcodec.vpxdec=2 android.qemud=1 qemu.avd_name=Pixel_2_API_30 ndns=4 mac80211_hwsim.radios=0
generic_x86:/ #
3.2.5 参考:/proc/bootconfig
官方解释:在 Android 12 中,bootconfig 功能取代了 Android 11 及更低版本中使用的
androidboot.*
内核命令行选项。 bootconfig 功能是一种将配置详细信息从构建和引导加载程序传递到 Android 12 的机制。此功能提供了一种将 Android 用户空间的配置参数与内核的配置参数分开的方法。将冗长的
androidboot.*
内核参数移动到 bootconfig 文件会在内核 cmdline 上创建空间,并使其可用于将来的扩展。说人话,就是读取Android 用户空间的配置参数, 把原来放在/proc/cmdline内的一些专属于android的配置,单独拿出来放到/proc/bootconfig
emulator64_x86_64_arm64:/ # cat /proc/bootconfig
androidboot.qemu = "1"
androidboot.qemu.cpuvulkan.version = "4202496"
androidboot.qemu.settings.system.screen_off_timeout = "2147483647"
androidboot.qemu.vsync = "60"
androidboot.qemu.gltransport.name = "pipe"
androidboot.qemu.gltransport.drawFlushInterval = "800"
androidboot.qemu.adb.pubkey = "QAAAAGmCOckn0styCNC22zGZCLweCnuYNoL//HHIwzDjI5y81wQ87MaYtaJuJWdTujKIaQrxz6Jx8vVeasge40/yNCUiLHW4GCI2krn0mM41JJbGPMewb2TjnMUX+m8ORzqVuGm+kCFVNDFPQd+ID9sL/vihVmfMij1N3obU4F1YuoC7964PuJT7sqVJVIxUwD3AbNRmh
v4zLSQoriStqTI2baeR6lcjeDOs/O1DqtnopB7BetGLqvF+aGC+huEai6VfgLsEqUlSPA1Ce5saF4u0vDypxmm6ax6w2q2D3BwCV5qphRg30Nelxb9pj7NvxSybJdVVkZSydM9zoitv8hJAFudUL1y+JeUeWH5iard/0vWp6iEBYnzmqKMlouIJBdXXkYRo/hQrXs/EDGYCrDEJe/vQdA2b3zbsvVDAxdc7tFpnU
46/c1Jjl5QLh+eReaHXmWASuuyIqD7WdeyBTfBeh1LRyAvqpjo8+qDP9t3MoIEXKjCgi/vi8lo5TFZQZ0FJ7XG1/ddtVzRH2bmAbVKQHR+rrRJFuUPUFSi3pamHjVkqAW7geY07gdD2V5RrxpO2NvbqdUgeGg7qmD6C5c1Pguija6r0/egIgaxUb0wIX0QLpCtqLqMv0Ed4cWg5ixZP4np+hI/u2EWyRgK8wi5Hi
jXwDux2JTMphw4PZeEvzbrLyhH5CgEAAQA= @unknown"
androidboot.qemu.camera_protocol_ver = "1"
androidboot.qemu.camera_hq_edge_processing = "0"
androidboot.qemu.virtiowifi = "1"
androidboot.qemu.hwcodec.avcdec = "2"
androidboot.qemu.hwcodec.vpxdec = "2"
androidboot.qemu.avd_name = "Pixel_2_API_31"
androidboot.hardware = "ranchu"
androidboot.serialno = "EMULATOR31X2X8X0"
androidboot.veritymode = "enforcing"
androidboot.opengles.version = "131072"
androidboot.logcat = "*:V"
androidboot.dalvik.vm.heapsize = "512m"
androidboot.vbmeta.size = "6272"
androidboot.vbmeta.hash_alg = "sha256"
androidboot.vbmeta.digest = "ea7166a14990ff9f4a2c9ed2cc7e869d4cde33137c4531a187242c84f42032a0"
androidboot.boot_devices = "pci0000:00/0000:00:03.0"
emulator64_x86_64_arm64:/ #
3.2.6 参考:SetStdioToDevNull
(1) SetStdioToDevNull
- 将标准输入(STDIN_FILENO)、输出(STDOUT_FILENO)、错误(STDERR_FILENO)重定向到 /dev/null
// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
// console enabled and no initramfs, otherwise it does not provide any fds for stdin/stdout/stderr.
// SetStdioToDevNull() is used to close these existing fds if they exist and replace them with
// /dev/null regardless.
//
// In the case that these fds are provided by the kernel, the exec of second stage init causes an
// SELinux denial as it does not have access to /dev/console. In the case that they are not
// provided, exec of any further process is potentially dangerous as the first fd's opened by that
// process will take the stdin/stdout/stderr fileno's, which can cause issues if printf(), etc is
// then used by that process.
//
// Lastly, simply calling SetStdioToDevNull() in first stage init is not enough, since first
// stage init still runs in kernel context, future child processes will not have permissions to
// access any fds that it opens, including the one opened below for /dev/null. Therefore,
// SetStdioToDevNull() must be called again in second stage init.
void SetStdioToDevNull(char** argv) {// Make stdin/stdout/stderr all point to /dev/null.int fd = open("/dev/null", O_RDWR); // NOLINT(android-cloexec-open)if (fd == -1) {int saved_errno = errno;android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);errno = saved_errno;PLOG(FATAL) << "Couldn't open /dev/null";}dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);if (fd > STDERR_FILENO) close(fd);
}
(2) dup2
int dup2(int old_fd, int new_fd) {// If old_fd is equal to new_fd and a valid file descriptor, dup2 returns// old_fd without closing it. This is not true of dup3, so we have to// handle this case ourselves.if (old_fd == new_fd) {if (fcntl(old_fd, F_GETFD) == -1) {return -1;}return old_fd;}return FDTRACK_CREATE(__dup3(old_fd, new_fd, 0));
}
四、篇尾
未完待续……
我的Android进阶修炼:安卓启动流程之init(1)相关推荐
- 源码详解Android 9.0(P) 系统启动流程之SystemServer
源码详解Android 9.0(P) 系统启动流程目录: 源码详解Android 9.0(P)系统启动流程之init进程(第一阶段) 源码详解Android 9.0(P)系统启动流程之init进程(第 ...
- android启动流程之preloader--->lk
关于异常的基本知识 什么是异常 对于AArch64而言,exception是指cpu的某些异常状态或者一些系统的事件(可能来自外部,也可能来自内部),这些状态或者事件可以导致cpu去执行一些预先设定的 ...
- Android筑基——Activity的启动过程之同进程在一个Activity中启动另一个Activity(基于api21)
目录 1. 前言 2. 正文 2.1 Activity类的startActivity()方法 2.2 Instrumentation类的execStartActivity()方法 2.3 Activi ...
- android启动流程之lk,Android系统之LK启动流程分析(一)
1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...
- Android平台WIFI启动流程之二
http://blog.sina.com.cn/s/blog_13146f9590101wji1.html [摘要] 本文从用户界面出发,从应用层到硬件适配层,对Android平台wifi启动和关闭的 ...
- android启动过程之init.rc文件浅析
1. init.rc文件结构 文件位置: init.c : /system/core/init init.rc : /system/core/rootdir 首先init.rc文件是以模块为单位 ...
- 【Android进阶学习】Http编程之HttpClient
在Android开发中,Android SDK附带了Apache的HttpClient,它是一个完善的客户端.它提供了对HTTP协议的全面支持,可以使用HttpClient的对象来执行HTTP GET ...
- Linux启动流程之ROM-CODE
1.从哪里开始? 下图是AM335X核心板和功能框图: AM335X核心板的存储信息如下: AM335X核心板运行linux系统,在这里提出一个问题: 上电后指令从哪里开始执行? DDR or EMM ...
- (连载)Android系统源码分析--Android系统启动流程之Linux内核
> **这是一个连载的博文系列,我将持续为大家提供尽可能透彻的Android源码分析 [github连载地址](https://github.com/foxleezh/AOSP/issues/3 ...
最新文章
- PetShop之表示层设计 - 《解剖PetShop》系列之六
- Web App适配iPhoneX
- Fabio技术手册(2):部署
- GNU make manual 翻译( 一百五十九)
- Java编程中值得注意的对象引用现象
- matlab二元一次方程求解_高中化学二元混合物的十字交叉法解法
- 查看linux服务器的配置
- 支付宝五福活动抢先开始了!原来今年可以提前集
- 03K个数或第k个数算法
- 关于matlab中get和set的用法
- 质量管理:PDCA循环
- Java奇数与偶数的判断
- DSP TMS320C5509A 控制DDS AD9854芯片进行FSK调制
- osm 搭建离线地图_搭建开源地图服务 - 利用OSMGIS和iD
- Largest prime factor
- 如何降低图片kb大小?教你两个快速压缩图片大小的方法!
- 基于MobileNetv3实现人脸面部表情识别
- [WPF]如何实现设置弹出窗口的Owner属性功能?
- Shader山下(十六)坐标空间与转换矩阵
- 如何用计算机模拟基金收益,[原创]中国股市十七年的基金计算机模拟