11.2.1. init天子一号进程

核心源码

Source

Path

init.rc

system/core/rootdir/init.rc

init.cpp

system/core/init/init.cpp

main.cpp

system/core/init/main.cpp

first_stage_init.cpp

system/core/init/first_stage_init.cpp

property_service.cpp

system/core/init/property_service.cpp

parser.cpp

system/core/init/parser.cpp

log.cpp

system/core/base/logging.cpp

service.cpp

system/core/init/service.cpp

signal_handler.cpp

system/core/init/signal_handler.cpp

action.cpp

system/core/init/action.cpp

builtins.cpp

system/core/init/builtins.cpp

selinux.cpp

system/core/init/selinux.cpp

系统启动

  • 上电后bootrom代码运行,引导bootloader程序

  • bootloader运行,把OS拉起来

  • linux内核启动后,在文件系统中寻找 init 文件,启动init进程

  • 启动 Zygote 进程,创建JavaVM并为JavaVM注册JNI,创建服务端socket

  • 启动 SystemServer 进程,启动Binder线程池和SystemServiceManager,并且启动各种系统服务

  • 启动Launcher,SystemServer启动的ActivityManagerServer会负责启动Launcher,Launcher启动后会将已安装的应用快捷图标显示到界面上

启动框架图

../../_images/init.png

init进程

注解

init进程,它是 内核启动的第一个用户级进程 ,进程号为1,android所以进程的共同始祖都是init

//system/core/init/main.cpp

int main(int argc, char** argv) {
    //判断是否需要启动ueventd
    //ueventd主要负责设备节点的创建,权限设定等一系列工作
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        //参数为subcontext,初始化日志系统
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

            return SubcontextMain(argc, argv, &function_map);
        }
        //启动selinux安全策略
        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }
        //启动init进程第二阶段
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }
    //默认启动init进程第一阶段
    return FirstStageMain(argc, argv);
}

ueventd

用于管理/dev目录下的设备节点,以及当硬件设备插入或者拔出时,系统负责处理用户空间对应的事件.在Android中对应的用户空间程序就是 ueventd .

从全局来看,ueventd启动时,会为所有当前注册的设备重新生成uevent,主要就是遍历/sys目录中的uevent文件,并且向其写入add,从而导致内核生成并且重新发送 uevent事件信息给所有注册的设备.

注解

为什么要这么做,因为这些设备注册的时候ueventd还没有运行,所以无法接收到完整的设备信息.所有要重新激活以便

11.2.1.1. init进程—第一阶段

//system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();  //init crash时重启引导加载程序
    }

    //用于记录启动时间
    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));
    // 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));
    //挂载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
    // Don't expose the raw commandline to unprivileged processes.
    //收紧cmdline的权限
    CHECKCALL(chmod("/proc/cmdline", 0440));
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    //挂载sysfs文件系统
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    //挂载selinuxfs文件系统
    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)));
    }

    //创建设备节点
    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.
    //创建可供读写的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.
    //创建可供读写的product目录
    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"));
#undef CHECKCALL

    ... ...

 }

这部分主要用于创建和挂载启动时所需的文件目录

int FirstStageMain(int argc, char **argv) {
    ... ...
    /*01. 创建设备节点,挂载文件系统*/
    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
    // talk to the outside world...
    //把标准输入,输出和错误重定向到到/dev/null
    SetStdioToDevNull(argv);
    //初始化kernel log系统,供用户打印log
    InitKernelLogging(argv);
    LOG(INFO) << "init first stage started!";

    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;

    //加载linux kernel modules
    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) {
        StartConsole();
    }

    //检查uboot传参,androidboot.force_normal_boot=1是否存在,若存在则创建first_stage_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";
        }
        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.
    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);
        }
    }

    //挂载fstab中定义的分区设备
    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }

    struct stat new_root_info;
    if (stat("/", &new_root_info) != 0) {
        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);
    }

    //初始化安全框架: Android Verify Boot, AVB主要用于防止文件系统本身被篡改
    //还包含了防止系统回滚的功能,以免有人回滚系统并利用以前的漏洞
    SetInitAvbVersionInRecovery();

    setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),1);

    //启动init进程,传入参数selinux_setup,执行命令/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));

    return 1;
}

11.2.1.2. init进程—第二阶段

init第二阶段主要是启动属性服务,解析init.rc文件并启动相关进程

SetupSelinux

int SetupSelinux(char** argv) {
    //重定向输入输出
    SetStdioToDevNull(argv);
    //初始化kernel log
    InitKernelLogging(argv);

    //init crash时重启引导加载程序
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        //发送signal重启系统
        InstallRebootSignalHandlers();
    }
    boot_clock::time_point start_time = boot_clock::now();

    MountMissingSystemPartitions();

    // Set up SELinux, loading the SELinux policy.
    //设置selinux log回调函数
    SelinuxSetupKernelLogging();
    //初始化selinux,导入selinux策略
    SelinuxInitialize();

    // We're in the kernel domain and want to transition to the init domain.  File systems that
    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
    // but other file systems do.  In particular, this is needed for ramdisks such as the
    // recovery image for A/B devices.
    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
    }

    //启动第二阶段程序
    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

    // execv() only returns if an error happened, in which case we
    // panic and never return from this function.

    return 1;
}

SecondStageMain

int SecondStageMain(int argc, char** argv) {
    //init crash时重启系统
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
    boot_clock::time_point start_time = boot_clock::now();
    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
    //重定向输入输出
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";
    // Init should not crash because of a dependence on any other process, therefore we ignore
    // SIGPIPE and handle EPIPE at the call site directly.  Note that setting a signal to SIG_IGN
    // is inherited across exec, but custom signal handlers are not.  Since we do not want to
    // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
    {
        struct sigaction action = {.sa_flags = SA_RESTART};
        action.sa_handler = [](int) {};
        sigaction(SIGPIPE, &action, nullptr);
    }
    // Set init and its forked children's oom_adj.
    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();
    }
    // Set up a session keyring that all processes will have access to. It
    // will hold things like FBE encryption keys. No process should override
    // its session keyring.
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    // See if need to load debug props to allow adb root, when the device is unlocked.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    bool load_debug_prop = false;
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }
    unsetenv("INIT_FORCE_DEBUGGABLE");
    //初始化属性系统,并从指定文件读取属性, 如/system/etc/selinux/plat_property_contexts
    PropertyInit();
    // Umount the debug ramdisk after property service has read the .prop files when it means to.
    if (load_debug_prop) {
        UmountDebugRamdisk();
    }
    // Mount extra filesystems required during second stage init
    MountExtraFilesystems();
    // Now set up SELinux for second stage.
    //完成第二阶段的selinux相关工作
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    //回复一些文件安全上下文
    SelinuxRestoreContext();
    //创建epoll并初始化子进程终止信号处理函数
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }
    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    //设置其他系统属性并开启系统属性服务
    StartPropertyService(&property_fd);
    ... ...
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    LoadBootScripts(am, sm);
    //解析init.rc等文件,建立rc文件的action,service,启动其他进程
    ... ...

    return 0;
 }

注解

Windows平台上有一个注册表管理器,注册表的内容采用键值对的形式来记录用户,软件的一些使用信息,即使系统或者软件重启也不会丢失. Android提供了类似的机制,叫做属性服务.主要代码为 property_init(); //初始化系统属性 start_property_service(); //开启属性服务

注解

init是一个 守护进程 ,为了防止init的子进程结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的 子进程占用程序表的空间.(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    //构建解析器
    Parser parser = CreateParser(action_manager, service_list);
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        //依次解析/system, system_ext, product, odm, vendor下的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("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}