文章目录

  • 我的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. 是用户空间的1号进程,所有其他进程的父进程
  2. 解析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)相关推荐

  1. 源码详解Android 9.0(P) 系统启动流程之SystemServer

    源码详解Android 9.0(P) 系统启动流程目录: 源码详解Android 9.0(P)系统启动流程之init进程(第一阶段) 源码详解Android 9.0(P)系统启动流程之init进程(第 ...

  2. android启动流程之preloader--->lk

    关于异常的基本知识 什么是异常 对于AArch64而言,exception是指cpu的某些异常状态或者一些系统的事件(可能来自外部,也可能来自内部),这些状态或者事件可以导致cpu去执行一些预先设定的 ...

  3. Android筑基——Activity的启动过程之同进程在一个Activity中启动另一个Activity(基于api21)

    目录 1. 前言 2. 正文 2.1 Activity类的startActivity()方法 2.2 Instrumentation类的execStartActivity()方法 2.3 Activi ...

  4. android启动流程之lk,Android系统之LK启动流程分析(一)

    1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...

  5. Android平台WIFI启动流程之二

    http://blog.sina.com.cn/s/blog_13146f9590101wji1.html [摘要] 本文从用户界面出发,从应用层到硬件适配层,对Android平台wifi启动和关闭的 ...

  6. android启动过程之init.rc文件浅析

    1.  init.rc文件结构 文件位置: init.c  : /system/core/init init.rc  : /system/core/rootdir 首先init.rc文件是以模块为单位 ...

  7. 【Android进阶学习】Http编程之HttpClient

    在Android开发中,Android SDK附带了Apache的HttpClient,它是一个完善的客户端.它提供了对HTTP协议的全面支持,可以使用HttpClient的对象来执行HTTP GET ...

  8. Linux启动流程之ROM-CODE

    1.从哪里开始? 下图是AM335X核心板和功能框图: AM335X核心板的存储信息如下: AM335X核心板运行linux系统,在这里提出一个问题: 上电后指令从哪里开始执行? DDR or EMM ...

  9. (连载)Android系统源码分析--Android系统启动流程之Linux内核

    > **这是一个连载的博文系列,我将持续为大家提供尽可能透彻的Android源码分析 [github连载地址](https://github.com/foxleezh/AOSP/issues/3 ...

最新文章

  1. PetShop之表示层设计 - 《解剖PetShop》系列之六
  2. Web App适配iPhoneX
  3. Fabio技术手册(2):部署
  4. GNU make manual 翻译( 一百五十九)
  5. Java编程中值得注意的对象引用现象
  6. matlab二元一次方程求解_高中化学二元混合物的十字交叉法解法
  7. 查看linux服务器的配置
  8. 支付宝五福活动抢先开始了!原来今年可以提前集
  9. 03K个数或第k个数算法
  10. 关于matlab中get和set的用法
  11. 质量管理:PDCA循环
  12. Java奇数与偶数的判断
  13. DSP TMS320C5509A 控制DDS AD9854芯片进行FSK调制
  14. osm 搭建离线地图_搭建开源地图服务 - 利用OSMGIS和iD
  15. Largest prime factor
  16. 如何降低图片kb大小?教你两个快速压缩图片大小的方法!
  17. 基于MobileNetv3实现人脸面部表情识别
  18. [WPF]如何实现设置弹出窗口的Owner属性功能?
  19. Shader山下(十六)坐标空间与转换矩阵
  20. 如何用计算机模拟基金收益,[原创]中国股市十七年的基金计算机模拟

热门文章

  1. Java分解质因数(每个非素数(合数)都写成几个素数(也可称为质数)相乘的形式)
  2. 市场调研团体怎么使用无人系统生产更安全
  3. 医学影像大数据与智能医疗
  4. antvue table 表格实现 首行搜索
  5. 一个iptables shell脚本
  6. 页面加载前需要定义全局变量
  7. 显示器连接vdc服务器,基于数字化校园的图像分析型VDC服务器的设计与实现
  8. omp parallel vs. omp parallel for 区别
  9. 智慧政务说明书 公文管理(三)
  10. VideoView视频尺寸的问题