Android Linux Zygote启动

·  阅读 200

Android init 启动

Android启动的第一个进程是由Linux Kernel启动的init进程。在init进程启动的过程中,会通过解析init.rc文件来启动zygote进程。

zygote进程是Android所在Java层的第一个进程,接下来我们一起来看一下zygote的启动过程。

.rc文件语法

如果你直接打开init.rc文件看它的内容,你大概率会一脸懵逼。因为它使用了一套独有的语法。

首先来了解一下.rc语法。它内部使用的是Android Init Language,俗称Android初始化语言

在上篇文章中其实已经提及过,它的语法主要分为五类,分别为:ActionsCommandsServicesOptionsImports

Actions

Actions称为命令序列。

它结合trigger触发器一起使用。主要用于确定该Actions作用的时机,当发生时机与某个Actionstrigger相匹配时,这个Actions将会添加到执行的队列中,队列中的每个Actions都按顺序出队,并且该Actions中的每个命令都按顺序执行。

Actions的格式如下:

on <trigger> [&& <trigger>]*
   <command>
   <command>
   <command>
复制代码

on开头,配合对应的trigger,一旦trigger触发,将会执行下面的command

trigger主要包含以下几种类型:

  1. early-init: 在初始化早期阶段触发
  2. init: 在初始化阶段触发
  3. late-init: 在初始化晚期阶段触发
  4. boot/charger: 当系统启动/充电时触发,还包含其他情况,此处不一一列举
  5. property:<key>=<value>: 当属性值满足条件时触发

来看一个示例:

on boot
   setprop a 1
   setprop b 2
 
on boot && property:true=true
   setprop c 1
   setprop d 2
 
on boot
   setprop e 1
   setprop f 2
复制代码

boot触发并假设属性true等于true时,将触发上面的Actions, 执行的命令顺序为:

setprop a 1
setprop b 2
setprop c 1
setprop d 2
setprop e 1
setprop f 2
复制代码

Commands

Commands俗称指令。

就是Linux需要执行的具体内容。

Commands包含的指令还是很多的,这里列举一些常用的指令。

  1. class_start <service_class_name>: 启动属于同一个class的所有服务
  2. start <service_name>: 启动指定的服务,若已启动则跳过
  3. stop <service_name>: 停止正在运行的服务
  4. setprop <name> <value>:设置属性值
  5. mkdir <path>:创建指定目录
  6. symlink <target> <sym_link>: 创建连接到<target><sym_link>符号链接
  7. write <path> <string>: 向文件path中写入字符串
  8. execfork并执行,会阻塞init进程直到程序完毕
  9. exprot <name> <name>:设定环境变量
  10. loglevel <level>:设置log级别

这个很简单,遇到就直接对照查看就可以了,例如上面提到的setprop,它就是用来对属性赋值的。

Services

Services俗称服务。

它由init进程启动,一般运行在init的子进程中,启动服务时首先会判断该服务对应的文件是否存在,而服务定义在.rc文件中,会通过init进程fork出子进程来启动对应的Service

具体的定义格式如下:

service <name> <pathname> [ <argument> ]*
   <option>
   <option>
   ...
复制代码

例如:

service servicemanager /system/bin/servicemanager
复制代码

代表的意义是:该服务的名称是servicemanager,对应的执行路径为/system/bin/servicemanager

Options

Options俗称可选项。

它都作用于Services,改变服务的运行方式与对应的时间等。

它的内容与Commands一样有很多,这里列举一些常用的,遇到直接查看就可以了。

  1. disabled: 不随class自动启动,只有根据service名才启动
  2. oneshot: service退出后不再重启
  3. user/group: 设置执行服务的用户/用户组,默认都是root
  4. class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default
  5. onrestart:当服务重启时执行相应命令
  6. socket: 创建名为/dev/socket/<name>socket
  7. critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

default: 意味着disabled=falseoneshot=falsecritical=false

Imports

Imports俗称导入。

用途是用来导入对应的.rc文件。

具体格式为:

import <path>
复制代码

例如

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
复制代码

Zygote

现在已经对.rc语法有了一个初步的了解,我们再来看init.rc的源文件,来分析一下它是如何启动Zygote进程的。

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000

    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
    write /sys/fs/selinux/checkreqprot 0

    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    setcon u:r:init:s0

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys

    start ueventd

    # create mountpoints
    mkdir /mnt 0775 root system

on init
    sysclktz 0

    loglevel 3

    ...
    ...

on late-init
   ...
   ...

on post-fs //挂载文件系统
   
   ...
   ...

on post-fs-data //挂载data
   ...
   ...

on boot //启动核心服务
   ...
   ...

   class_start very-first

on property:androVM.inited=1
    class_start core
    class_start main

on property:genymotion.local_opengl.started=1
    start surfaceflinger

on nonencrypted
    class_start main
    class_start late_start

复制代码

以上是各个触发器的执行顺序。满足条件会执行class_start来启动名称为main的类,并启动它关联的service

其中class_start对应的就是builtins.cpp中的do_class_start。它们之间的映射关系是由BuiltinFunctionMap维护的

const BuiltinFunctionMap& GetBuiltinFunctionMap() {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const BuiltinFunctionMap builtin_functions = {
        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
        {"chmod",                   {2,     2,    {true,   do_chmod}}},
        {"chown",                   {2,     3,    {true,   do_chown}}},
        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
        {"class_reset_post_data",   {1,     1,    {false,  do_class_reset_post_data}}},
        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
        {"class_start",             {1,     1,    {false,  do_class_start}}},
        {"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},
        ...
        ...
   }
}
复制代码

它与对应的Actions绑定是在init.cpp中的SecondStageMain方法里面。

const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
复制代码

system/core/init/builtins.cpp

所以我们直接看do_class_start

static Result<void> do_class_start(const BuiltinArguments& args) {
    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
        return {};
    // Starting a class does not start services which are explicitly disabled.
    // They must  be started individually.
    for (const auto& service : ServiceList::GetInstance()) {
        if (service->classnames().count(args[1])) {
            if (auto result = service->StartIfNotDisabled(); !result.ok()) {
                LOG(ERROR) << "Could not start service '" << service->name()
                           << "' as part of class '" << args[1] << "': " << result.error();
            }
        }
    }
    return {};
}
复制代码

在这方法中会通过ServiceList::GetInstance来获取解析.rc文件中的Service链表,通过匹配对应的classnames来找到对应的Service

根据上面的class_start main,说明要找的是class名称为main这个关联的Service

这个main service位于init.zygote.rc中,内部源码如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

复制代码

它只注册了一个service,名称为zygote,对应的执行文件为/system/bin/app_process, 后面的--zygote --start-system-serve都是它的启动参数。

通过class main设置它的启动类为main,通过它来启动这个service

这里正好对应的上面需要寻找的类名为main的启动类来启动service,也就是说启动的这个service就是zygote

至于service的解析过程在之前的文章init启动有提及过,它是通过LoadBootScripts(am, sm)进入解析.rc文件,然后内部会通过service_parser.cpp来解析对应的service语句,最后将解析的service信息添加到service vector链表中。

service_list_->AddService(std::move(service_));
 
vector<_Tp, _Allocator>::__emplace_back_slow_path(_Args&&... __args)
复制代码

找到匹配的main class之后,会进入StartIfNotDisabled

system/core/init/service.cpp

Result<void> Service::StartIfNotDisabled() {
    if (!(flags_ & SVC_DISABLED)) {
        return Start();
    } else {
        flags_ |= SVC_DISABLED_START;
    }
    return {};
}
复制代码

如果它不是disabled就是调用Start,通过上面的service注册信息是没有设置disabled,所以为false。即进入Start启动service

Result<void> Service::Start() {
 
    ...
    ...

    if (flags_ & SVC_RUNNING) {
        if ((flags_ & SVC_ONESHOT) && disabled) {
            flags_ |= SVC_RESTART;
        }
        // 如果没有错误就启动之前启动的service
        reboot_on_failure.Disable();
        return {};
    }

    。。。

    // 判断该service对于的执行文件是否存在
    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        flags_ |= SVC_DISABLED;
        return ErrnoError() << "Cannot find '" << args_[0] << "'";
    }

    ...

    pid_t pid = -1;
    if (namespaces_.flags) {
        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
    } else {
        // fork()创建子进程
        pid = fork();
    }
 
    if (pid == 0) { // fork的子进程
        umask(077);
 
        ...
        ...
 
        if (!ExpandArgsAndExecv(args_, sigstop_)) {
            PLOG(ERROR) << "cannot execv('" << args_[0]
                        << "'). See the 'Debugging init' section of init's README.md for tips";
        }
 
        _exit(127);
    }

    ...

    NotifyStateChange("running");
    reboot_on_failure.Disable();
    return {};
}

复制代码

Start中首先会判断该service是否已经启动,若没有,然后去验证该service是否能够找到对应的可执行文件,这里对应的就是/system/bin/app_process。 最后会通过fork()来创建子进程,并返回pid = 0

所以当pid === 0时,说明运行在子进程中,然后进入ExpandArgsAndExecv方法。

static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
    std::vector<std::string> expanded_args;
    std::vector<char*> c_strings;
 
    expanded_args.resize(args.size());
    c_strings.push_back(const_cast<char*>(args[0].data()));
    for (std::size_t i = 1; i < args.size(); ++i) {
        auto expanded_arg = ExpandProps(args[i]);
        if (!expanded_arg.ok()) {
            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
        }
        expanded_args[i] = *expanded_arg;
        c_strings.push_back(expanded_args[i].data());
    }
    c_strings.push_back(nullptr);

    if (sigstop) {
        kill(getpid(), SIGSTOP);
    }
 
    // 启动zygote
    return execv(c_strings[0], c_strings.data()) == 0;
}
复制代码

ExpandArgsAndExecv方法中最后会通过execv来启动子进程,对应的是进入app_main.cpp并调用它的main方法。

zygote是通过forkexecv共同创建的。

再来看app_main.cpp做了什么

frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
    ...
    ...
    
    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
 
    ...

    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            // 启动的是zygote
            zygote = true;
            // 设置进程名称,这里为zygote
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            // 启动完zygote之后,需要开启system service
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    ...

    if (zygote) {
        // 启动java ZygoteInit
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

复制代码

核心部分就两步,第一步是解析service中的参数,这个参数是在.rc中注册时的参数,第二步通过runtime.start来启动java中的ZygoteInit

这一步是Linux首次进入到Java层。

为了避免单篇幅度过长,关于Linux如何启动Java中的ZygoteInitZygoteInit的内部启动过程,这块的具体内容将会在下篇文章继续分析。

最后总结一下zygoteLinux中的创建过程:

  1. 通过LoadBootScripts(am, sm)解析.rc文件
  2. 解析完之后,根据Actions事件触发时机找到类名为main的启动类
  3. 根据main启动类来启动对应的zygote服务
  4. 然后通过fork来创建zygote子进程,并返回pid = 0
  5. 通过execv来启动zygote子进程,进入app_main.cppmain方法

方法 6. 最后再解析注册表中service的参数并通过runtime.start启动java层的ZygoteInit

一图胜千文

项目

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用android-startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。 与此同时android-startup支持同步与异步等待,并通过有向无环图拓扑排序的方式来保证内部依赖组件的初始化顺序。

AwesomeGithub: 基于Github客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于Jetpack&DataBindingMVVM;项目中使用了ArouterRetrofitCoroutineGlideDaggerHilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。

分类:
Android
标签: