10

Android Linux Zygote启动

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzIzNTc5NDY4Nw%3D%3D&%3Bmid=2247484903&%3Bidx=1&%3Bsn=b55b5bd01ad292256364ecd965ca2288
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Android init 启动

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

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

.rc文件语法

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

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

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

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. exec
    fork
    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. oneshotservice 退出后不再重启
  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.cpp
    main
    
  6. service
    runtime.start
    java
    ZygoteInit
    

一图胜千文

V7zeM3e.png!mobile

推荐阅读

进阶必备的工具

Jetpack:DataStore必知的几个优点

算法之旅:复杂度分析

你的点赞我将开心一整天

N7zqQb2.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK