<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();