<Android开发> Android内核系统开发-启动过程详解(第2部分 系统关键服务的启动简析)

本文深入剖析Android系统启动过程中ServiceManager和Zygote的关键服务启动,包括ServiceManager作为Binder机制中的DNS服务器角色,以及Zygote如何根据不同架构启动,孵化新进程和服务。
摘要由CSDN通过智能技术生成

<Android开发> Android内核系统开发-启动过程详解(第2部分 系统关键服务的启动简析)

继上一篇介绍语法后,接下来详细看看init进程解析init.rc后启动的一些关键的系统服务进程,其中最重要的有ServiceManager、Zygote和SystemServer。
由于设计的内容较多,作者整理了流程图,l流程图内容也比较多,文章中放不下,就单独放到一个链接中:流程图;读者自行下载,配合文章阅读,能更加清晰理解整个流程。
一、Android的“DNS服务器”--------ServiceManager
ServiceManager是Binder机制中的“DNS服务器”,负责域名(某Binder服务在ServiceManager注册时提供的名称) 到IP地址(有底层Binder驱动分配的值)的解析。
在高通8155 LA.1.1 中 ServiceManager是在init.rc里由init进程启动。如下:

/* system\core\rootdir\init.rc */
on init                                                                         #init事件
	.......# Start logd before any other services run to ensure we capture all of their logs.
    # 在任何其他服务运行之前启动 logd 以确保我们捕获它们的所有日志。
    start logd              # 这个命令将启动一个服务 logd ,如果它没有处于运行状态的话

    # Start essential services.
    # 启动基本服务。
    start servicemanager        # 这个命令将启动一个服务 servicemanager ,如果它没有处于运行状态的话
    start hwservicemanager      # 这个命令将启动一个服务 hwservicemanager ,如果它没有处于运行状态的话
    start vndservicemanager     # 这个命令将启动一个服务 vndservicemanager ,如果它没有处于运行状态的话
    ......

有servicemanager文件可看出,servicemanager是一个 Linux程序的执行文件。它在设备中的存储路径是“/system/bin/servicemanager”(需要进入root才能查询到,如下图),源码路径则是“frameworks/native/cmds/servicemanager”。
在这里插入图片描述
servicemanager的启动描述则在servicemanager.rc文件里,.rc文件位于“frameworks/native/cmds/servicemanager/servicemanager.rc”。高通8155的LA.1.1基线代码对于servicemanager的描述和启动的位置分在两个不同的.rc文件。servicemanager.rc具体启动描述内容如下:

/*frameworks/native/cmds/servicemanager/servicemanager.rc*/
service servicemanager /system/bin/servicemanager
    class core animation                    #所属类是  core 和 animation
    user system                             #在启动服务前将用户切换至 system ,默认是root
    group system readproc                   #在启动服务前将用户组切换至 system 和 readproc
    critical                                #表明这是对设备至关重要的一个服务,如果4分钟内退出超过4次,则设备将重启进入恢复模式
    onrestart restart healthd               #当servicemanager重启时,重启  healthd 进程
    onrestart restart zygote                #当servicemanager重启时,重启  zygote 进程
    onrestart restart audioserver           #当servicemanager重启时,重启  audioserver 进程
    onrestart restart media                 #当servicemanager重启时,重启  media 进程
    onrestart restart surfaceflinger        #当servicemanager重启时,重启  surfaceflinger 进程
    onrestart restart inputflinger          #当servicemanager重启时,重启  inputflinger 进程
    onrestart restart drm                   #当servicemanager重启时,重启  drm 进程
    onrestart restart cameraserver          #当servicemanager重启时,重启  cameraserver 进程
    onrestart restart keystore              #当servicemanager重启时,重启  keystore 进程
    onrestart restart gatekeeperd           #当servicemanager重启时,重启  gatekeeperd 进程
    onrestart restart thermalservice        #当servicemanager重启时,重启  thermalservice 进程
    writepid /dev/cpuset/system-background/tasks # 当fork一个子进程时,写子进程的pid到一个给定的文件。是给cgroup/cpuset使用
    
    #设置服务进程的关闭行为。如果未指定此选项,则在关闭过程中通过使用SIGTERM和SIGKILL终止服务。
    #shutdown_behavior为"critical"时,服务在关闭期间不会被杀死,直到关闭超时为止。
    #当服务进程关闭超时时,即使标记为"shutdown critical"的服务也将被杀死。
    #当shutdown行为启动时,标有"shutdown critical"的服务未运行时,它将启动。
    shutdown critical                       #服务在关闭期间不会被杀死,直到关闭超时为止

由servicemanager.rc可看出,servicemanager所属class是core,其它同类的系统进程包括ueventd、console(/system/bin/sh)、adbd等。根据core组的特性,这些进程会同时被启动或停止。另外,critical选项说明它是系统的关键进程,意味着如果进程不幸在4分钟异常退出4次,则设备将重启进入还原模式。每当servicemanager重启时,其它关键进程也会被重启,如:healthd、zygote、audioserver、media、surfaceflinger、inputflinger、drm、cameraserver、keystore、gatekeeperd、thermalservice等。

在这里插入图片描述

二、Zygote—“孕育”新的线程和进程
Zygote这个词的字面意思时“受精卵”,因而可以“孕育”出一个“新生命”。在Android中大多数应用进程和系统进程都是通过Zygote来生成的。下面具体分析Zygote时如何启动的。
同servicemanager类似,Zygote也是由init解析rc脚本时启动的。在早期的Android版本中Zygote的启动命令是直接被写在init.rc中。但随着硬件等的不断升级换代,Android系统不得不面对32位和64位机器同时存在的状况,因而,对Zygote的启动也需要根据不同的情况区分对待,在高通平台中,在init.rc中加载Zygote的脚本描述如下:

/* system/core/rootdir/init.rc */
import /init.environ.rc
import /init.usb.rc
import /init.${
   ro.hardware}.rc
import /vendor/etc/init/hw/init.${
   ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${
   ro.zygote}.rc

根据系统属性ro.zygote的具体值,init.rc中加载不同的描述zygote的rc脚本,zygote的描述脚本包含“init.zygote32_64.rc”、“init.zygote32.rc”、“init.zygote64_32.rc”、“init.zygote64.rc”,具体文件如下:(目录:system/core/rootdir)
在这里插入图片描述
其中zygote32和zygote64分别对应32位和64位机器的情况,而zygote32_64和zygote64_32,则是Primary Arch和Secondary Arch的组合。
组合介绍如下:

路径:device/qcom/msmnile_au/BoardConfig.mk
BoardConfig.mk用于指定目标平台相关的不少属性,咱们能够在这个脚本中同时指定Primary和Secondary的CPU Arch和ABI:

与Primary Arch相关的变量有TARGET_ARCH、TARGET_ARCH_VARIANT、TARGET_CPU_VARIANT等,具体范例以下:

/* device/qcom/msmnile_au/BoardConfig.mk */
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_ABI := arm64-v8a
TARGET_CPU_ABI2 :=
TARGET_CPU_VARIANT := generic

与Secondary Arch相关的变量有TARGET_2ND_ARCH、TARGET_2ND_ARCH_VARIANT、TARGET_2ND_CPU_VARIANT等,具体范例以下:

/* device/qcom/msmnile_au/BoardConfig.mk */
TARGET_2ND_ARCH := arm
TARGET_2ND_ARCH_VARIANT := armv7-a-neon
TARGET_2ND_CPU_ABI := armeabi-v7a
TARGET_2ND_CPU_ABI2 := armeabi
TARGET_2ND_CPU_VARIANT := cortex-a9

所以,由BoardConfig.mk可知,高通8155的LA.1.1基线中的“init.rc“-》“import /init.${ro.zygote}.rc“-》ro.zygote的值为“zygote64_32”,即Primary Arch为64,Secondary Arch为32。接下来详细看zygote64_32的内容,如下:

/* system/core/rootdir/init.zygote64_32.rc */
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main                                              #所属类是  main
    priority -20                                            #设置服务进程的优先级.优先级取值范围为-20~19,默认是0.可以通过setpriority()设置
    user root                                               #在启动服务前将用户切换至 root ,默认是 root
    group root readproc reserved_disk                       #在启动服务前将用户组切换至 root 和 readproc 和 reserved_disk
    socket zygote stream 660 root system                    #创建一个名为 /dev/socket/zygote 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
    socket usap_pool_primary stream 660 root system         #创建一个名为 /dev/socket/usap_pool_primary 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
    onrestart write /sys/android_power/request_state wake   #当 zygote 重启时,写  wake 到 /sys/android_power/request_state
    onrestart write /sys/power/state on                     #当 zygote 重启时,写  on   到 /sys/power/state
    onrestart restart audioserver                           #当 zygote 重启时,重启  audioserver 进程
    onrestart restart cameraserver                          #当 zygote 重启时,重启  cameraserver 进程
    onrestart restart media                                 #当 zygote 重启时,重启  media 进程
    onrestart restart netd                                  #当 zygote 重启时,重启  netd 进程
    onrestart restart wificond                              #当 zygote 重启时,重启  wificond 进程
    writepid /dev/cpuset/foreground/tasks                   # 当fork一个子进程时,写子进程的pid到一个给定的文件。是给cgroup/cpuset使用

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main                                          #所属类是  main
    priority -20                                        #设置服务进程的优先级.优先级取值范围为-20~19,默认是0.可以通过setpriority()设置
    user root                                           #在启动服务前将用户切换至 root ,默认是 root
    group root readproc reserved_disk                   #在启动服务前将用户组切换至 root 和 readproc 和 reserved_disk
    socket zygote_secondary stream 660 root system      #创建一个名为 /dev/socket/zygote_secondary    的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
    socket usap_pool_secondary stream 660 root system   #创建一个名为 /dev/socket/usap_pool_secondary 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
    onrestart restart zygote                            #当 zygote_secondary 重启时,重启  zygote 进程
    writepid /dev/cpuset/foreground/tasks               # 当fork一个子进程时,写子进程的pid到一个给定的文件。是给cgroup/cpuset使用

从上面这段脚本描述可以看出:
存在两个服务 zygote 和 zygote_secondary;
zygote的path:/system/bin/app_process64
zygote_secondary的path:/system/bin/app_process32
zygote的Arguments:-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
zygote_secondary的Arguments:-Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload

zygote和zygote_secondary所属class都为main,而不是core。同class的系统进程有netd、debuggerd、rild等。从两者的路径可以看出,它们所在的程序名分别叫“ app_process64” 和 “ app_process32”,而不像ServiceManager一样在一个独立的程序中。通过指定–zygote 参数,app_process可以识别出用户是否需要启动zygote。那么app_process又是什么东东呢?app_process的源码路径在:frameworks/base/cmds/app_process中,先看看其Android.mk的内容,如下:

/* frameworks/base/cmds/app_process/Android.mk */
LOCAL_PATH:= $(call my-dir)

app_process_common_shared_libs := \
    libandroid_runtime \
    libbinder \
    libcutils \
    libdl \
    libhidlbase \
    liblog \
    libnativeloader \
    libutils \

# This is a list of libraries that need to be included in order to avoid
# bad apps. This prevents a library from having a mismatch when resolving
# new/delete from an app shared library.
# See b/21032018 for more details.
# 这是为了避免不良应用程序需要包含的库列表。 这可以防止库在从应用程序共享库中解析新/删除时出现不匹配。
# 详情请参阅 b/21032018。
app_process_common_shared_libs += \
    libwilhelm \

app_process_common_static_libs := \
    libsigchain \

app_process_src_files := \
    app_main.cpp \

app_process_cflags := \
    -Wall -Werror -Wunused -Wunreachable-code

app_process_ldflags_32 := \
    -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
app_process_ldflags_64 := \
    -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= $(app_process_src_files)

LOCAL_LDFLAGS_32 := $(app_process_ldflags_32)    #32位程序的ld标志
LOCAL_LDFLAGS_64 := $(app_process_ldflags_64)    #64位程序的ld标志

LOCAL_SHARED_LIBRARIES := $(app_process_common_shared_libs)         #共享库

LOCAL_WHOLE_STATIC_LIBRARIES := $(app_process_common_static_libs)   #静态库

LOCAL_MODULE:= app_process              #模块名称
LOCAL_MULTILIB := both                  #用于针对的硬件平台架构,可选项:"both":同时   "32":针对32  "64":针对64   " ":由系统根据其它变量决定
LOCAL_MODULE_STEM_32 := app_process32   #指定32位系统下的应用程序
LOCAL_MODULE_STEM_64 := app_process64   #指定64位系统下的应用程序

LOCAL_CFLAGS += $(app_process_cflags)

# In SANITIZE_LITE mode, we create the sanitized binary in a separate location (but reuse
# the same module). Using the same module also works around an issue with make: binaries
# that depend on sanitized libraries will be relinked, even if they set LOCAL_SANITIZE := never.
#
# Also pull in the asanwrapper helper.

# 在 SANITIZE_LITE 模式下,我们在单独的位置创建经过清理的二进制文件(但重用相同的模块)。 
# 使用相同的模块也可以解决 make 的问题:依赖于已清理库的二进制文件将被重新链接,即使它们设置 LOCAL_SANITIZE := never。
# 同时引入 asanwrapper 助手。
ifeq ($(SANITIZE_LITE),true)
LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
LOCAL_REQUIRED_MODULES := asanwrapper
endif

include $(BUILD_EXECUTABLE)

# Create a symlink from app_process to app_process32 or 64
# depending on the target configuration.
# 根据目标配置创建从 app_process 到 app_process32 或 64 的符号链接。
ifneq ($(SANITIZE_LITE),true)
include  $(BUILD_SYSTEM)/executable_prefer_symlink.mk
endif

从上面的描述可看到,app_process其实扮演的类似一个外壳,实际是去调用 app_process64 或app_process32程序。那么app_process具体内容有哪些呢?下面分析app_process.cpp中的main主函数的实现,内容如下:

/* frameworks/base/cmds/app_process/app_main.cpp */
int main(int argc, char* const argv[])
{
   
    /*主要用于调试时的日志输出,输出app_process 程序运行时传入的所有参数*/
    if (!LOG_NDEBUG) {
   
      String8 argv_String;
      for (int i = 0; i < argc; ++i) {
   
        argv_String.append("\"");
        argv_String.append(argv[i]);
        argv_String.append("\" ");
      }
      ALOGV("app_process main with argv: %s", argv_String.string());
    }

    //Android运行时环境
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments  处理命令行参数
    // ignore argv[0]   忽略 argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm.
    //
    // The first argument after the VM args is the "parent dir", which
    // is currently unused.
    //
    // After the parent dir, we expect one or more the following internal
    // arguments :
    //
    // --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.
    //
    // For non zygote starts, these arguments will be followed by
    // the main class name. All remaining arguments are passed to
    // the main method of this class.
    //
    // For zygote starts, all remaining arguments are passed to the zygote.
    // main function.
    //
    // Note that we must copy argument string values since we will rewrite the
    // entire argument block when we apply the nice name to argv0.
    //
    // As an exception to the above rule, anything in "spaced commands"
    // goes to the vm even though it has a space in it.

    // 直到 '--' 或第一个非 '-' arg 的所有内容都进入 vm。
    // VM args 后面的第一个参数是“父目录”,目前未使用。
    // 在父目录之后,我们期望一个或多个以下内部参数:
    // --zygote : 以 zygote 模式启动
    // --start-system-server : 启动系统服务器。
    // --application : 以应用程序(独立,非 zygote)模式启动。
    // --nice-name : 这个进程的好名字。
    // 对于非 zygote 启动,这些参数后跟主类名。所有剩余的参数都传递给这个类的 main 方法。
    // 对于 zygote 启动,所有剩余的参数都传递给 zygote。
    // 主功能。
    // 请注意,我们必须复制参数字符串值,因为当我们将 nice 名称应用于 argv0 时,我们将重写整个参数块。
    // 作为上述规则的一个例外,“间隔命令”中的任何内容都会进入 vm,即使其中有空格。
    const char* spaced_commands[] = {
    "-cp", "-classpath" };
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    // 允许“间隔命令”通过恰好 1 个参数(不管 -s)。
    bool known_command = false;

    int i;
    for (i = 0; i < argc; i++) {
   
        if (known_command == true) {
               //判断参数是否存在
          runtime.addOption(strdup(argv[i]));   //将参数传给runtime
          // The static analyzer gets upset that we don't ever free the above
          // string. Since the allocation is from main, leaking it doesn't seem
          // problematic. NOLINTNEXTLINE
          // 静态分析器对我们没有释放上面的字符串感到不安。 由于分配来自 main,因此泄漏它似乎没有问题。 NOLINTNEXTLINE
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
   
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
          //比较判断argv中的参数是否存在 和 spaced_commands中一样的部分
            known_command = true;                               //标记阐述存在
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
   
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
   
            ++i; // Skip --.        跳过 --开头的
            break;
        }

        runtime.addOption(strdup(argv[i]));  //将参数传给runtime
        // The static analyzer gets upset that we don't ever free the above
        // string. Since the allocation is from main, leaking it doesn't seem
        // problematic. NOLINTNEXTLINE
        // 静态分析器对我们没有释放上面的字符串感到不安。 由于分配来自 main,因此泄漏它似乎没有问题。 NOLINTNEXTLINE
        ALOGV("app_process main add option '%s'", argv[i]);
    }

    // Parse runtime arguments.  Stop at first unrecognized option.
    // 解析运行时参数。 在第一个无法识别的选项处停止。
    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
            zygote = true;                                      //标记
            niceName = ZYGOTE_NICE_NAME;                        //别名
        } else if (strcmp(arg, "--start-system-server") == 0) {
    //判断参数存在  --start-system-server   表示需要启动 system server
            startSystemServer = true;                           //标记
        } else if (strcmp(arg, "--application") == 0) {
            //判断参数存在  --application   表示启动进入独立的程序模式
            application = true;                                 //标记
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
        //判断参数存在  --nice-name=   表示设置进程别名为 = 后面的字串
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
                   //判断参数存在  --   表示设置className 增加 --后面字串 作为数据
            className.setTo(arg);
            break;
        } else {
   
            --i;        //倒叙检索参数
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        //判断 非zygote
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        // 我们不是在 zygote 模式下,我们需要传递给 RuntimeInit 的唯一参数是应用程序参数。
         // args 的剩余部分被传递给启动类 main()。 在我们用进程名称覆盖它们之前制作它们的副本。
        args.add(application ? String8("application") : String8("tool"));  //如果参数 包含 application 则添加application作为数据 否则为tool
        runtime.setClassNameAndArgs(className, argc - i, argv + i); //设置Android运行时环境 runtime 的类名和参数

        //调试信息,输出类名和参数
        if (!LOG_NDEBUG) {
   
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
   
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else {
   
        // We're in zygote mode.
        // 我们处于 zygote 模式。
        maybeCreateDalvikCache();

        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

waterAdmin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值