Android系统之Init进程

242 阅读5分钟

本文基于Android10.0源码分析

按下电源的那一刻

当按下电源的时候,引导芯片会从ROM预定义的位置(这里一般会是一个独立的ROM:Boot ROM,存着BootLoader的代码)将BootLoader加载到RAM中,BootLoader职责相当于PC的BIOS,也即引导程序。BootLoader的终极目标就是将操作系统拉起运行,即将kernel加载到内存中,并跳转到固定的kernel入口,从那里开始初始化。

Kernel

当BootLoader将kernel加载到RAM后,便从固定的入口开始执行内核代码。这里我尝试去找了下固定的入口是哪,没找到...这里我们对kernel初始化的代码要被迫忽略细节了。那么忽略细节后,根据其他文章,在启动Init进程之前,还有两个进程被启动:

  • swapper进程(0号进程):又称为idle进程,系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Binder Driver、Display、Camera Driver等相关工作。
  • kthreadd进程(2号进程):Linux系统的内核进程,是所有内核进程的鼻祖,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。

Init进程-第一个用户进程

Linux内核加载完毕后,会首先启动Init进程,并从下面这段代码开始,源码位于system/core/init/main.cpp

int main(int argc, char** argv) {
    
    // init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        // 进入InitLogging 和SubcontextMain     
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

            return SubcontextMain(argc, argv, &function_map);
        }
        // 加载selinux规则,并设置selinux日志,完成SELinux相关工作
        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }
        // 启动第二阶段
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }
    // 启动第一阶段    
    return FirstStageMain(argc, argv);
}

并按照FirstStageMain -> SetupSeLinux -> SecondStageMain -> ueventd_main顺序执行

FirstStageMain

int FirstStageMain(int argc, char** argv) {

    // Clear the umask.
    umask(0);

    //初始化系统环境变量
    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));

    // 挂载及创建基本的文件系统,并设置合适的访问权限
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
    ...
    // 不要将原始命令行公开给非特权进程
    CHECKCALL(chmod("/proc/cmdline", 0440));
    ...
    //将内核的stdin/stdout/stderr 全都重定向/dev/null,关闭默认控制台输出
    SetStdioToDevNull(argv);
    
    //初始化内核log
    InitKernelLogging(argv);

    //挂载 system、cache、data 等系统分区
    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }
    ...
    //进入下一步,SetupSelinux
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));
    return 1;

}

我们来总结一下,FirstStageMain到底做了哪些重要的事情:

  1. 挂载及创建基本的文件系统,并设置合适的访问权限
  2. 关闭默认控制台输出,并初始化内核级log。
  3. 挂载 system、cache、data 等系统分区
  4. execv跳转到SetupSelinux执行

SetupSelinux主要是SELinux安全策略,跟本文无关,略过,这里最后是调用execv跳转到了SecondStageMain

SecondStageMain

int SecondStageMain(int argc, char** argv) {

    // 禁止OOM killer 杀死该进程以及它的子进程
    if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {...}
    
    //初始化属性服务,并从指定文件读取属性
    property_init();
    ...
    //初始化Epoll,android这里对epoll做了一层封装
    Epoll epoll;
    if (auto result = epoll.Open(); !result) {
        PLOG(FATAL) << result.error();
    }

    //epoll 中注册signalfd,主要是为了创建handler处理子进程终止信号
    InstallSignalFdHandler(&epoll);
    ...
    //epoll 中注册property_set_fd,设置其他系统属性并开启系统属性服务
    StartPropertyService(&epoll);
    ...
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    //解析init.rc等文件,建立rc文件的action 、service,启动其他进程, 非常关键的一步
    LoadBootScripts(am, sm);
    ...
    // 省略一些init.rc中触发器的调用
    am.QueueEventTrigger("init");
    am.QueueEventTrigger("late-init");
    ...
    while (true) {
         ...
        // 循环等待事件发生
        if (auto result = epoll.Wait(epoll_timeout); !result) {
            LOG(ERROR) << result.error();
        }
    }
    return 0;
}
 

第二阶段做了以下这些比较重要的事情:

  1. 初始化属性服务,并从指定文件读取属性(重要)
  2. 初始化epoll
  3. 解析init.rc文件,并按rc里的定义去启动服务(最重要)
  4. 开启死循环,用于接收epoll的事件

init.rc

关于rc(run command)文件我们不做介绍,init.rc完成了一些非常重要的动作,我们只需要知道做了哪些事情就好。

在调用init action时,会启动一些核心服务,其中包括logdservicemanager

on init
   ...
   start logd
   start servicemanager
   ...

在调用late-init时,会执行zygote-start服务

on late-init
    ...
    trigger zygote-start
    ...

zygote-start指向对应的zygote.rc

service zygote /system/bin/app_process -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
    socket usap_pool_primary stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver //当zygote重启时,则会重启audioserver, 下同
    onrestart restart cameraserver 
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

zygote.rc是如何fork zyogte进程的呢?zygote.rc文件最终会被转化成一个service对象(如下代码), 将rc里配置的内容全部一一对应写到了service里,并在合适的时机调用service.start,start里便会fork新的进程,子进程即为zyogte进程,并通过exec跳转到/system/bin/app_process去执行。

Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                 const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
                 const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
                 const std::vector<std::string>& args)
    : name_(name),
      classnames_({"default"}),
      flags_(flags),
      pid_(0),
      crash_count_(0),
      uid_(uid),
      gid_(gid),
      supp_gids_(supp_gids),
      namespace_flags_(namespace_flags),
      seclabel_(seclabel),
      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                 "onrestart", {}),
      ioprio_class_(IoSchedClass_NONE),
      ioprio_pri_(0),
      priority_(0),
      oom_score_adjust_(-1000),
      start_order_(0),
      args_(args) {}

那么此时呢,作为子进程的zygote已经跳转到了/system/bin/app_process,而init进程便在死循环中等待epoll消息,并在zygote子进程意外退出后,提供重启能力(避免其成为僵尸进程)。我们一般处理僵尸进程是通过父进程wait,而这里没有,因为父进程还需要在死循环中等待epoll消息,所以这里用了另外的手段,至于什么手段我们忽略,那么此时呢,Init可以说是已经完成他的工作,将目光拉到了Zygote这个更重要的进程上。

OK,我们整体总结下这里本篇文章提到的Init的流程

  1. 启动电源,通过Boot RomBootLoader载入到RAM中
  2. BootLoader将kernel载入到RAM中,并从跳转到kernel的入口去执行
  3. kernel创建第一个进程:swapper(idle)进程,并由swapper进程初始化进程管理、内存管理、加载驱动的工作
  4. kernel创建第二个进程:kthreadd进程,作为内核进程,并由其fork出kworkder工作线程,ksoftirqd软中断线程,thermal等内核守护进程。
  5. 启动第一个用户进程Init
  6. Init进程将初始化步骤细分按照FirstStageMain -> SetupSeLinux -> SecondStageMain 顺序执行
  7. FirstStageMain挂载及创建基本的文件系统,并设置合适的访问权限
  8. SetupLinux初始化SELinux的安全策略
  9. SecondStageMain初始化属性服务、epoll、运行init.rc、Init进程开始死循环等待epoll数据。
  10. 通过init.rc,初始化各种核心服务进程(logd、servicemanager)、启动进程zygote,并exec跳转到zygote进程入口

最后我们用一张流程图来再次描述下本过程。

image.png

Ok,接下来介绍Zygote进程

感谢大佬们的文章: