Init 介绍

Init 进程是 Android 系统中用户空间的第一个进程(pid=1),它是用户进程的鼻祖,负责孵化各种属性服务、守护进程也包括 Zygote。Init 是由多个源文件共同组成的,这些文件位于 /system/core/init

启动过程

Kernel 启动找到 Init 进程后,进程入口为源码 init 目录下的 main.cpp 的 main()。

/system/core/init/main.cpp
int main(int argc, char** argv) {
    ...
        
    // init进程创建子进程ueventd,负责设备节点的创建、权限设定等
    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);  // Step2,对Selinux初始化
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);  // Step3,解析init.rc文件、提供服务、创建epoll与处理子进程的终止等
        }
    }

    return FirstStageMain(argc, argv);  // Step1,挂载相关文件系统
}

FirstStageMain

/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
    // 将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,
    // 一旦监听到这些信号,即执行重启系统
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        // 处理Init进程挂掉的情况,会重启bootloader
        InstallRebootSignalHandlers();  
    }
    
    ...
    
    // Clear the umask.
    umask(0);    
    
    // 设置环境变量地址
    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    // 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.
    // 挂载tmpfs文件系统
    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)
    // 挂载proc文件系统
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
    // 不要将原始命令行公开给非特权进程,root也只有只读权限
    CHECKCALL(chmod("/proc/cmdline", 0440));
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);
    // 不要将原始bootconfig公开给非特权进程
    chmod("/proc/bootconfig", 0440);
    std::string bootconfig;
    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));  // 设置用户组
    // 挂载sysfs文件系统
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
    
    // 提前创建了kmsg设备节点文件,用于输出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)));
    }    
    
    // 创建Linux伪随机设备
    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));    
    
    // log wrapper所必需的,需要在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));
    // /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"))   
    
        // First stage init stores Mainline sepolicy here.
    CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
    
    // 将内核的stdin/stdout/stderr全部重定向/dev/null,关闭默认控制台输出
    SetStdioToDevNull(argv);
    // tmpfs已经挂载到/dev上,同时也挂载了/dev/kmsg,可以和外界沟通了       
    // 初始化日志系统
    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!";  
    
    ...    
   
    // 如果该文件存在且设备已经解锁,则允许adb root指令(userdebug sepolicy)
    if (access("/force_debuggable", F_OK) == 0) {
        constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
        constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
        std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.
        if (access(adb_debug_prop_src, F_OK) == 0 &&
            !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {
            LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
                         << ": " << ec.message();
        }
        if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
            !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {
            LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
                         << kDebugRamdiskSEPolicy << ": " << ec.message();
        }
        // setenv for second-stage init to read above kDebugRamdisk* files.
        setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
    }    
    
    ...
    
    // 挂载system、cache、data等系统分区
    if (!DoFirstStageMount(!created_devices)) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    } 
    
    ... 
   
    // 进入下一步,SetupSelinux
    const char* path = "/system/bin/init";  // 找到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来启动Init进程
    
    // 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;
}

主要通过 mount 挂载对应的文件系统,mkdir 创建对应的文件目录,并配置相应的访问权限。

这些文件只是在应用运行的时候存在,一旦应用运行结束就会随着应用一起消失。

挂载的文件系统主要有四类:

  1. tmpfs:一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中。由于 tmpfs 是驻留在 RAM 的,因此它的内容是不持久的。断电后,tmpfs 的内容就消失了。
  2. devpts:为伪终端提供了一个标准接口,它的标准挂接点是 /dev/pts。只要 pty 的主复合设备 /dev/ptmx 被打开,就会在 /dev/pts 下动态地创建一个新的 pty 设备文件。
  3. proc:也是一个虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。
  4. sysfs:与 proc 文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在 /sys 目录下。作用是把系统的设备和总线按层次组织起来,使得它们可以在用户空间读取,用来向用户空间导出内核的数据结构和属性。

在 FirstStageMain 还会通过 InitKernelLogging(argv) 来初始化 log 日志系统。此时 Android 还没有自己的系统日志,采用 kernel 的 log 系统,打开的设备节点 /dev/kmsg, 可通过 cat /dev/kmsg 来获取内核 log。

最后会通过 execv 方法传递对应的 path 与下一阶段的参数 selinux_setup。

SetupSelinux

/system/core/init/selinux.cpp
int SetupSelinux(char** argv) {
    ...        
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

    return 1;
}

SetupSelinux 方法中加载了 selinux 的策略并启动 selinux 的强制模式,然后启动了 Init 进程。Init 的二进制文件存放在机器的 /system/bin/init,然后通过 execv 启动 Init 进程第二阶段。

SecondStageMain

int SecondStageMain(int argc, char** argv) {
    ...
    SetStdioToDevNull(argv);
    // 初始化本阶段内核日志
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

    ...

    // 禁止OOM Killer杀死该进程以及它的子进程
    if (auto result =
                WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
        !result.ok()) {
        LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
                   << " to /proc/1/oom_score_adj: " << result.error();
    }

    // 设置所有进程都能访问的会话密钥
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

    // 创建/dev/.booting文件,一个标记,表示booting进行中
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

    ...
        
    // 初始化属性服务,并从指定文件读取属性
    // 使用mmap共享内存,/dev/__properties__/property_info
    PropertyInit();

    ...

    // 进行Selinux第二阶段,并恢复一些文件安全上下文
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();

    // 初始化epoll,android这里对epoll做了一层封装
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }

    // 使用epoll对Init子进程的信号进行监听
    // epoll中注册signalfd,主要是为了创建handler处理子进程终止信号
    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    // 开启属性服务,并注册到epoll中
    StartPropertyService(&property_fd);

    ...

    // 会执行/system/bin/init subcontext
    InitializeSubcontext();

    // 为解析init.rc中的action和service做准备
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    // 加载系统启动脚本init.rc
    LoadBootScripts(am, sm);

    ...

    // cgroups用于控制资源,cpuset相关
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
    // 执行rc文件中触发器为on early-init的语句
    am.QueueEventTrigger("early-init");

    // 等冷插拔设备初始化完成
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    // 设备组合键的初始化操作
    Keychords keychords;
    am.QueueBuiltinAction(
            [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
                for (const auto& svc : ServiceList::GetInstance()) {
                    keychords.Register(svc->keycodes());
                }
                keychords.Start(&epoll, HandleKeychord);
                return {};
            },
            "KeychordInit");

    // 执行rc文件中触发器为on init的语句
    am.QueueEventTrigger("init");

    // 当设备处于充电模式时,不需要mount文件系统或者启动系统服务。
    // 充电模式下,将charger设为执行队列,否则把late-init设为执行队列
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

    // 基于属性当前状态,运行所有的属性触发器
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    // Restore prio before main loop
    setpriority(PRIO_PROCESS, 0, 0);
    while (true) {
        // 进入死循环状态
        auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};

        auto shutdown_command = shutdown_state.CheckShutdown();
        if (shutdown_command) {
            LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                      << "' Calling HandlePowerctlMessage()";
            HandlePowerctlMessage(*shutdown_command);
            shutdown_state.set_do_shutdown(false);
        }

        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
        if (!IsShuttingDown()) {
            // 重启死掉的子进程
            auto next_process_action_time = HandleProcessActions();

            // If there's a process that needs restarting, wake up in time for that.
            if (next_process_action_time) {
                epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                        *next_process_action_time - boot_clock::now());
                if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
            }
        }

        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout = 0ms;
        }

        // 循环等待事件发生
        auto pending_functions = epoll.Wait(epoll_timeout);
        if (!pending_functions.ok()) {
            LOG(ERROR) << pending_functions.error();
        } else if (!pending_functions->empty()) {
            ...
        }
    }

    return 0;
}

LoadBootScripts() 会加载 init.rc 配置文件,之后加载 /{system, vendor, odm}/etc/init/ 下(Android 设备中的目录)的所有 rc 配置文件。

LoadBootScripts

/system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    // 初始化ServiceParse、ActionParser、ImportParser三个解析器
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        // 解析init.rc文件
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

    // 加载解析Service语句的解析器
    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
        	&service_list, GetSubcontext(), std::nullopt));
    // 加载解析on语句的解析器
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
    // 加载解析import语句的解析器
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

init.rc

init.rc 有两个,分别位于:

/system/core/rootdir/init.rc,正常启动

/bootable/recovery/etc/init.rc,刷机

import

import /init.environ.rc		# 导入全局环境变量
import /system/etc/init/hw/init.usb.rc		# adb服务、USB相关内容的定义
import /init.${ro.hardware}.rc		# 硬件相关的初始化,一般是厂商定制
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc		# 定义Zygote服务

init.rc 中会根据系统的不同属性来引入不同的 zygote 脚本。

  • init.zygote32.rc:zygote 进程对应的执行程序是 app_process(纯32bit模式)
  • init.zygote64.rc:zygote 进程对应的执行程序是 app_process(纯64bit模式)
  • init.zygote64_32.rc:启动两个 zygote 进程(zygote 和 zygote_secondary),对应的执行程序分别是 app_process64(主模式),app_process32

on early-init

on early-init
	...
	start ueventd
	exec_start apexd-bootstrap
	...

early-init 中启动了 ueventd 服务和 apex 相关服务。

  • ueventd服务

    service ueventd    # ueventd服务的可执行文件的路径为/system/bin/ueventd
        class core    # ueventd归属于core class,同样归属于core class的还有adbd、console等服务
        critical    # 表明这个Service对设备至关重要,如果Service在四分钟内退出超过4次,则设备将重启进入恢复模式。
        seclabel u:r:ueventd:s0    # selinux相关的配置
        shutdown critical    # ueventd服务关闭行为
    
  • early-init触发时机

    /system/core/init.init.cpp$SecondStageMain
    
    am.QueueEventTrigger("early-init");
    

on init

on init
	...
	start logd.    # 用于保存Android运行期间的日志
	...
	start servicemanager    # Android系统服务管理者,负责查询和注册服务
	...

on late-init

on late-init
	# 启动vold服务(管理和控制Android平台外部存储设备,包括SD插拔、挂载、卸载、格式化等)
	trigger early-fs
	trigger factory-fs
    trigger fs
    trigger post-fs
    trigger late-fs

    # 挂载/data,启动apexd服务
    trigger post-fs-data

    # Should be before netd, but after apex, properties and logging is available.
    trigger load_bpf_programs

    # 启动zygote服务,在启动zygote服务之前会先启动netd服务(专门负责网络管理和控制的后台守护进程)
    trigger zygote-start

    # 移除/dev/.booting文件
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot
    trigger mmi
  • zygote

    /system/core/rootdir/init.zygote64.rc
    
    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
        class main
        priority -20
        user root
        group root readproc reserved_disk
        socket zygote stream 660 root system
        socket usap_pool_primary stream 660 root system
        onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
        onrestart write /sys/power/state on
        onrestart restart audioserver
        onrestart restart cameraserver
        onrestart restart media
        onrestart restart media.tuner
        onrestart restart netd
        onrestart restart wificond
        task_profiles ProcessCapacityHigh
        critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
    

    该文件通过 service 语句来创建 zygote 进程,该进程的代码位于 /system/bin/app_process 目录下。当相关的触发器被触发后,便会启动 zygote 进程。

总结

  • Init 入口中包含5个分支:

    • ueventd:实际上就是 Init 程序的软链接,在 init.rc 的 early-init 阶段启动
    • selinux_setup:FitstStageMain 中启动
    • subcontext:SecondStageMain 中启动
    • second_stage:在 selinux_setup 启动完之后执行
    • first_stage:默认首先执行
  • Init 进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用 SELinux 安全策略。

    Init 进程第二阶段的主要工作是初始化属性系统,解析 SELinux 的匹配规则,处理子进程终止信号,启动系统属性服务。

    Init 进程第三阶段主要是解析 init.rc 来启动其他进程,进入死循环,进行子进程实时监控。

  • 在 SecondStage,首先加载 init.rc 配置,然后再依次在 Android 设备中查找以下三种配置,并加载:

    • /system/etc/init/
    • /vendor/etc/init/
    • /odm/etc/init/
  • Android 根文件系统的镜像中不存在 /dev 目录,该目录是 Init 进程启动后动态创建的。为此,Init 进程创建子进程 ueventd,并将创建设备节点文件的工作托付给 ueventd。

    ueventd 通过两种方式创建设备节点文件:

    • 冷插拔(Cold Plug)

      以预先定义的设备信息为基础,当 ueventd 启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。

    • 热插拔(Hot Plug)

      在系统运行中,当有设备插入 USB 端口时,ueventd 就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。

  • signal:每个进程在处理其他进程发送的 signal 信号时都需要先注册,当进程的运行状态改变或终止时会产生某种 signal 信号,Init 进程是所有用户空间进程的父进程,当其子进程终止时产生 signal 信号,以便父进程进行处理,主要是为了防止子进程成为僵尸进程。

    僵尸进程:父进程使用 fork 创建子进程,子进程终止后,如果父进程不知道子进程已经终止的话,这时子进程虽然已经退出,但是在系统进程表中还为它保留了一些信息(如进程号、运行时间、退出状态等),这个子进程就是僵尸进程。系统进程表是一项有限的资源,如果它被僵尸进程耗尽的话,系统可能会无法创建新的进程。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐