Android 启动过程 - 从上电到init进程的建立

一、上电,启动BootLoader

在按下电源按钮后,会经历如下过程

CPU复位,并从一个固定地址(由CPU厂商指定)开始执行指令。该地址指向的是芯片上一块ROM(只读存储区),称为Boot Rom

Boot Rom:存储一小段程序,即Firmware,该程序会引导到一个预定义地址(该地址可通过软件改写),去加载BootLoader。

BootLoader:为加载Linux Kernel做准备,并最终加载Linux Kernel。在加载Kernel前做的准备如下:

检测外部RAM,并加载一段程序用于后面的准备工作

装载网络、内存模块,建立内存映射,为内核启动做准备

在Android中,BootLoader也即fastboot。厂商的锁也是在这里实现的。

准备工作做好后,启动内核。内核放在/boot分区下。

Kernel: 内核启动后,以start_kernel()为入口,会初始化系统cache、schedulers、安装内存、驱动、内核守护进程、挂载基本文件系统(/etc、/bin、/sbin、/lib、/dev等)、输入输出设备、进程表等等一些列准备工作,随后,启动两个重要进程:

init(pid=1): 用户空间进程的鼻祖。

kthreadd(pid=2):内核空间进程的鼻祖。

以kernel/arm64 - android-amber-intel-linux--android11分支为例:

kernel/arm64/init/main.c

asmlinkage __visible void __init start_kernel(void)
{
    ...
    cgroup_init_early();
    ...
    boot_cpu_init();
    page_address_init();
    mm_init_cpumask(&init_mm);
    setup_command_line(command_line);
    setup_nr_cpu_ids();
    setup_per_cpu_areas();
    boot_cpu_state_init();
    ...
    page_alloc_init();
    ..
    mm_init_cpumask(&init_mm);
    sched_init();
    ...
    kmem_cache_init_late();
    ...
    fork_init(); // 指定最大进程数
    ...
    signals_init();
    ...
    cpuset_init();
    cgroup_init();
    ...
    rest_init(); // 启动init和kthreadd进程
}

static noinline void __init_refok rest_init(void)
{
    ...
    kernel_thread(kernel_init, NULL, CLONE_FS); // 启动init进程,此时pid=1
    ...
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);// 启动kthreadd进程,此时pid=2。见kernel/arm64/kernel/kthread.c
    ...
}

static int __ref kernel_init(void *unused)
{
    ...
    // 从以下目录中,一次查找init可执行文件,如果找到则运行
    if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh")) {
		mnh_trace(MNH_TRACE_INIT_RUNNING);
		return 0;
	}
}

static int try_to_run_init_process(const char *init_filename)
{
	int ret;
	ret = run_init_process(init_filename);
	return ret;
}

static int run_init_process(const char *init_filename)
{
	argv_init[0] = init_filename;
    // 最终通过do_execve运行init可执行文件,启动init进程
	return do_execve(getname_kernel(init_filename),
		(const char __user *const __user *)argv_init,
		(const char __user *const __user *)envp_init);
}

如上,在经历start_kernel -> rest_init -> kernel_init的调用后,会遍历以下目录,寻找init可执行文件:

/sbin/init

/etc/init

/bin/init

/bin/sh

最终调用do_execve执行init文件。

这里需要注意的是,上述调动过程中,pid是在kernel_thread(kernel_init...)时创建的,do_execve使用新进程覆盖当前进程,pid不变。

二、Init进程

init进程是Linux的第一个用户空间进程。

init进程的入口:

core/init/init.cpp

int main(int argc, char** argv) {
    // Boost prio which will be restored later
    setpriority(PRIO_PROCESS, 0, -20);
    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);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

init可执行文件是一个多次被执行的文件。

1. kernel执行init文件,参数为空,执行FirstStageMain函数

core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    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));
    ...
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=1000"));
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    CHECKCALL(mkdir("/mnt/product", 0755));
    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=0"));
    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=0"))
    ...
    // 执行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));

主要做以下工作:

创建、挂载一系列基础文件目录、设置相关访问权限

在FirstStageMain中执行selinux_setup分支。

2. SetupSelinux中调用second_stage分支

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));
3. 执行second_stage分支,即SecondStageMain函数

core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    ...
    // 初始化自身及子进程oom_score_adj,DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST = -1000
    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();
    }
    
    ...
    // 初始化epoll
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }
    // 初始化
    InstallSignalFdHandler(&epoll);
    
    ...
    // 初始化subcontext分支
    InitializeSubcontext();
    ...
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    // 解析init.rc
    LoadBootScripts(am, sm);
    ...
    // 将一系列Action、Event放入队列依次执行
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    // 此处执行的是rc里的early-init事件
    am.QueueEventTrigger("early-init");
    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    ...
    
    // 处理epoll
     while (true) {
        // By default, sleep until something happens. Do not convert far_future into
        // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock
        // is 1ns.
        const boot_clock::time_point far_future = boot_clock::time_point::max();
        boot_clock::time_point next_action_time = far_future;

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

        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            // 执行队列里的EventTrigger, PropertyChange, BuiltinAction
            am.ExecuteOneCommand();
            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) {
                next_action_time = boot_clock::now();
            }
        }
        // Since the above code examined pending actions, no new actions must be
        // queued by the code between this line and the Epoll::Wait() call below
        // without calling WakeMainInitThread().
        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) {
                next_action_time = std::min(next_action_time, *next_process_action_time);
            }
        }

        std::optional<std::chrono::milliseconds> epoll_timeout;
        if (next_action_time != far_future) {
            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                    std::max(next_action_time - boot_clock::now(), 0ns));
        }
        auto epoll_result = epoll.Wait(epoll_timeout);
        if (!epoll_result.ok()) {
            LOG(ERROR) << epoll_result.error();
        }
        if (!IsShuttingDown()) {
            HandleControlMessages();
            SetUsbController();
        }
    }
}

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        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);
    }
}

在SecondStageMain中

1)初始化了Subcontext分支;

subcontext用于执行init交给其的一些任务(比如后面执行的某些Command),通过socket与init进行通信

2)执行了LoadBootScripts。LoadBootScripts中依次解析多个rc文件,其中,/system/etc/init/hw/init.rc是主文件。

在源码“core/init/README.md”中,有对rc文件的详细描述。

/system/etc/init/hw/init.rc被执行后,其他rc文件会被执行。README.md对于其他rc文件的

说明:

1. /system/etc/init/ is for core system items such as

   SurfaceFlinger, MediaService, and logd.

2. /vendor/etc/init/ is for SoC vendor items such as actions or

   daemons needed for core SoC functionality.

3. /odm/etc/init/ is for device manufacturer items such as

   actions or daemons needed for motion sensor or other peripheral

   functionality.

3)通过epoll和ActionManager的设计,将一些列事件放入队列,依次执行。

4. init.rc

 在/system/etc/init/hw/init.rc中,启动一系列核心进程和服务,包括重要的zygote进程。

init.rc说明:core/init/README.md

对zygote的启动:

on userspace-reboot-resume
  trigger userspace-reboot-fs-remount
  trigger post-fs-data
  trigger zygote-start
  trigger early-boot
  trigger boot

对ueventd进程的启动:

service ueventd /system/bin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0
    user root
    shutdown critical

/system/bin/ueventd是init的一个链接,即最终会执行core/init/init.cpp中ueventd分支。

ueventd是一个守护进程,主要作用是接收uevent来管理/dev/(设备节点)

至此,init所有分支已启动完毕。

  • 22
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值