Android 重学系列--系统启动到Activity
背景
为什么要写这个,是因为做android到今天,发现自己的基础不牢固,很多东西早年明白是怎么回事,下意识的知道怎么做,android这一块做久了过多的思考业务与设计,反而原理开始模模糊糊了。但是探究其原因,是过久没接触,成为了下意识行为。这是个不好的信号,也是一个不好的开始。最近又听闻在tx的哥们,以及面试大厂成功的哥们,一问起他们的面试题,以及工作情况,原以为自己的水平还算过的去,发现自己在腾讯连个t2等级的岗位都感觉很悬,真的是坐井观天。
于是今天我要端正态度,下定决心从头开始复习,为自己的基础打牢固,希望自己能够持之以恒,让自己走到更高的高度。那么就从我们最熟悉的四大组件的Activity开始吧。
从这次开始,我会将长博客分开几次来写。不会像之前的,一次性写完。主要是发现一口气写完数万字,可读性太差了。
正文
Activity的生命周期有七大生命周期:onCreate,onStart,onResume,onStop,onPause,onDestroy,onRestart.为什么这么划分,这么划分有什么好处,这些生命周期分别在做什么,在经历这些生命周期之前,Android做了又做了什么行为,这一切的一切值得让人探讨。
从系统启动到Activity。我们启动系统,到界面点击App的图标开始,研究Activity吧。
接下来,所有的源码都来自Android 7.0系统。
系统启动
从系统启动开始复习。我想起我当时的毕业设计,是第一次真正意义上的接触Linux系统。现在要把这些知识,从脑海中回忆整理出来。
Android系统的内核实际上是一个Linux内核,系统的一切启动,都是从Linux内核开始启动。
复习一下,Linux系统启动流程:
Linux启动流程大致分为2个步骤:
引导和启动。
引导:
分为BIOS上电子检(POST)和引导装载程序(GRUB)
启动
内核引导,内核init初始化,启动 systemd,其是所有进程之父。
Android系统启动
这里Android启动流程系统稍微有点不一样。
这里借别人的一幅图:
到了BootLoader内核引导程序的装载之后,会启动Linux内核。此时Linux内核就去启动第一个进程,init进程。
启动进程,那么一定会启动到init.cpp中的main函数。
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
parser.ParseConfig("/init.rc");
此时会通过Parser方法去解析当前目录下的init.rc文件中的语法。
文件开头会配置下面这个文件:
import /init.${ro.zygote}.rc
这里稍微提一下rc的语法,import的意思和js里面css的import意思很相近,就是解析这个文件里面的内容。
此时会判断当前系统的位数是多少。现在就以32位为例子。
此时对应的文件就会替换中间的变量变成init.zygote32.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 audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
此时rc的脚本,意思是service是启动一个服务名字是zygote,通过/system/bin/app_process。 此时为zygote创建socket,访问权限600,onrestrat是指当这些服务需要重新启动时,执行重新启动。更多相关可以去看看:/system/core/init/readme.txt
我们转到/frameworks/base/cmds/app_process/app_main.cpp
找到下个核心,Zygote启动主函数。
###Zygote(孵化进程)启动
Zygote从名字上就知道,这个初始进程的作用像一个母鸡一样,孵化所有的App的进程。
int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
// 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 = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
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;
}
}
Vector<String8> args;
if (!className.isEmpty()) {
// 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.
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
} else {
...
//设置启动模式
if (zygote) {
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.");
return 10;
}
}
实际上这里主要分两步:
第一步:解析刚才传进来的配置。
第二步:根据配置来决定app_main启动模式
根据我们的命令我们可以知道系统启动调用的命令会使得zygote标识位为true。
此时进入到ZygoteInit里面初始化。在这之前,我们看看这个runtime对象在start方法做了什么事情。AppRuntime继承于AndroidRuntime
文件:/frameworks/base/core/jni/AndroidRuntime.cpp
AndroidRuntime::start
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
ALOGD(">>>>>> START %s uid %d <<<<<<\n",
className != NULL ? className : "(unknown)", getuid());
static const String8 startSystemServer("start-system-server");
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}
//确认Android目录环境
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
//启动虚拟机
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
//注册jni方法
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
//准备好环境之后,为传入的选项转化为java 层的String对象
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
//根据传进来的class name,反射启动对应的类
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}
这个方法实际上包含了极大量的工作。此时我们能看到的是Zygote从app_main的入口想要启动ZyogteInit的类。我们看到这个签名就知道这是Android系统启动以来第一个要加载的Java类。那么此时我们必须保证虚拟机启动完成。
所以AndroidRunTime要进行的步骤一共为三大步骤:
1.初始化虚拟机
2.注册jni方法
3.反射启动ZygoteInit。
1.初始化虚拟机
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
我们来研究看看这几个方法究竟做了啥。
先看看jni_invocation的初始化方法
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
static const char* kDebuggableSystemProperty = "ro.debuggable";
#endif
static const char* kLibraryFallback = "libart.so";
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
char buffer[PROP_VALUE_MAX];
#else
char* buffer = NULL;
#endif
library = GetLibrary(library, buffer);
// Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
// This is due to the fact that it is possible that some threads might have yet to finish
// exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
// unloaded.
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
handle_ = dlopen(library, kDlopenFlags);
if (handle_ == NULL) {
if (strcmp(library, kLibraryFallback) == 0) {
// Nothing else to try.
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
// Note that this is enough to get something like the zygote
// running, we can't property_set here to fix this for the future
// because we are root and not the system user. See
// RuntimeInit.commonInit for where we fix up the property to
// avoid future fallbacks. http://b/11463182
ALOGW("Falling back from %s to %s after dlopen error: %s",
library, kLibraryFallback, dlerror());
library = kLibraryFallback;
handle_ = dlopen(library, kDlopenFlags);
if (handle_ == NULL) {
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
return true;
}
核心方法实际上只有一处:
library = GetLibrary(library, buffer);
handle_ = dlopen(library, kDlopenFlags);
dlopen是指读取某个链接库。这个library是通过getlibrary获得的。这个方法就不贴出来了。这个方法解释就是通过读取kLibrarySystemProperty下的属性,来确定加载什么so库。而这个so库就是我们的虚拟机so库。
如果对那些做过编译Android机子的朋友就能明白,实际上这个属性在/art/Android.mk中设定的。里面设定的字符串一般是libart.so或者libdvm.so.这两个分别代表这是art虚拟机还是dvm虚拟机。
当然这是允许自己编写虚拟机,自己设置的。因此,在这个方法的最后会检测,这个so是否包含以下三个函数:
- JNI_GetDefaultJavaVMInitArgs
- JNI_CreateJavaVM
- JNI_GetCreatedJavaVMs
只有包含这三个函数,Android初始化才会认可这个虚拟机做进一步的初始化工作。
这么实现很眼熟是不是?不错这个实现,实际上就和代理模式+工厂模式如出一辙。
这样在Android framework初始化的时候不必要关注虚拟机是什么,将初始化任务代理交给JniInvocation来完成。同时,Android.mk作为工厂来决定Android系统加载什么虚拟机。
因此,JniInvocation这个类抽离了三个方法出来:
JniInvocation::JniInvocation() :
handle_(NULL),
JNI_GetDefaultJavaVMInitArgs_(NULL),
JNI_CreateJavaVM_(NULL),
JNI_GetCreatedJavaVMs_(NULL) {
LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized");
jni_invocation_ = this;
}
如果不是这是几个so库无法通过接口实现来完成,那么这个设计模式uml如下:
这里我将不涉及虚拟机初始化的流程,我将会专门开一个文章来总结。
此时我们已经初始化好了art虚拟机(此时是Android 7.0),我们要开始启动虚拟机了。
2.启动化虚拟机
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
在startVm中做了两个工作,第一个把对虚拟机设置的参数设置进去,第二点,调用虚拟机的JNI_CreateJavaVM方法。核心方法如下:
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
此时如果创建成功,返回的int是大于等于0,小于0则是创建异常。并且把初始化好之后的数据类型赋值给JavaVm,JniEnv。
而下面这个函数onVmCreated(env);
void AndroidRuntime::onVmCreated(JNIEnv* env)
{
// If AndroidRuntime had anything to do here, we'd have done it in 'start'.
}
是不做任何处理的。这里等下说,还有什么妙用。
2.注册jni方法
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
我们看看这个注册是做什么的。
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
ATRACE_NAME("RegisterAndroidNatives");
/*
* This hook causes all future threads created in this process to be
* attached to the JavaVM. (This needs to go away in favor of JNI
* Attach calls.)
*/
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
ALOGV("--- registering native functions ---\n");
/*
* Every "register" function calls one or more things that return
* a local reference (e.g. FindClass). Because we haven't really
* started the VM yet, they're all getting stored in the base frame
* and never released. Use Push/Pop to manage the storage.
*/
env->PushLocalFrame(200);
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
//createJavaThread("fubar", quickTest, (void*) "hello");
return 0;
}
实际上这个核心方法是register_jni_procs。注册jni方法。
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
}
RegJNIRec这个是一个结构体。mProc就是每个方法的方法指针:
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
每一次都会调用注册这个数组中的方法。我们看看这个array的数组都有什么。
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
...
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
REG_JNI(register_android_hardware_camera2_DngCreator),
REG_JNI(register_android_hardware_Radio),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
REG_JNI(register_android_hardware_SoundTrigger),
REG_JNI(register_android_hardware_UsbDevice),
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
....
};
看到了把。这些方法无一不是我们熟悉的方法,什么Bitmap,Parcel,Camera等等api,这些需要调用native的类。
这就说回来了,onVmCreated这个留下的空函数做什么的。一般我们开发留下一个空函数一般不是兼容版本,就是交给用户做额外处理。实际我们开发jni的时候,我们可以通过这个方法,常做的两件事情,第一,创建一个JniEnv并为其绑定线程,第二,我们可以在这个时刻做好方法映射,让我们内部的方法,不需要遵守jni的命名规则(包+方法名),这样能够大大的扩展我们so的编写的可读性和复用。
3.反射启动ZygoteInit
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
此时就走到反射调用ZygoteInit流程,此时传进来的slashClassName就是ZygoteInit的包名。
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
启动结束,就释放掉,java层的String类,以及为JavaVM接触当前线程的绑定,并回收掉JavaVm内存。
ZygoteInit
此时进入到ZygoteInit的类中。我们看看main方法
文件:/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
try {
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
RuntimeInit.enableDdms();
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
//解析传进来的命令,主要是确定是否要启动SystemServer
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
//注册监听
registerZygoteSocket(socketName);
//准备资源
preload();
//装载好虚拟机之后,做一次gc
gcAndFinalize();
...
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
ZygoteHooks.stopZygoteNoThreadCreation();
//启动SystemServer
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
//启动循环
Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
这里做的事情主要是四步:
第一步:解析命令是否要启动SystemServer
第二步:registerZygoteSocket来打开socket监听命令,是否孵化新的进程
第三步:启动SystemServer
第四步:runSelectLoop 启动一个系统的looper循环
第一步很简单,就跳过。直接看看第二步registerZygoteSocket
private static void registerZygoteSocket(String socketName) {
if (sServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
sServerSocket = new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
通过创建一个文件描述符,对这个文件描述符进行监听,来做到一个本地socket的功能。
很有趣,没想到socket还能这么创建,平时我们使用socket的时候往往都是使用ServerSocket这些类去完成,没想到可以使用监听文件描述符来办到socket的监听,不过实际上想想Linux下一切皆文件,也就不再觉得意料之外。
之后这个sServerSocket对象将会作为Zygote作为监听外侧的耳目。
启动SystemServer
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException {
...
ZygoteConnection.Arguments parsedArgs = null;
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
//启动新的进程forkSystemServer
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);
}
return true;
}
这个方法分两步:
1.fork出SystemServer进程
2.初始化SystemServer
1.fork出SystemServer进程
这个步骤的核心方法是fork出SystemServer进程
我们稍微往这个方法下面看看,究竟有什么问题?
public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
VM_HOOKS.preFork();
int pid = nativeForkSystemServer(
uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true);
}
VM_HOOKS.postForkCommon();
}
核心方法是调用native方法进行fork。
文件com_android_internal_os_Zygote.cpp
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
debug_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
NULL, NULL);
if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
gSystemServerPid = pid;
// There is a slight window that the system server process has crashed
// but it went unnoticed because we haven't published its pid yet. So
// we recheck here just to make sure that all is well.
int status;
if (waitpid(pid, &status, WNOHANG) == pid) {
ALOGE("System server process %d has died. Restarting Zygote!", pid);
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
}
return pid;
}
ForkAndSpecializeCommon这个方法就是所有进程fork都会调用这个这个native函数。这个函数实际上是调用linux的fork函数来拷贝出新的进程。
2.初始化SystemServer进程
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);
}
孵化好新的进程。这里返回结果。加入结果pid是等于0.说明会走这个分支。我们这里只看一个孵化进程的情况。看看这个方法handleSystemServerProcess(parsedArgs);
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
closeServerSocket();
...
/*
* Pass the remaining arguments to SystemServer.
*/
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
/* should never reach here */
}
由于这个新的进程不需要接收消息,去孵化进程,所以第一件事情就是关闭socket的监听。第二件事就是通过RuntimeInit初始化SystemServer。
文件:com/android/internal/os/RuntimeInit.java
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}
此时nativeZygoteInit会初始化我们ProcessState时候,同时初始化我们鼎鼎大名的Binder驱动。
我们看看核心方法applicationInit做了啥:
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
...
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
就是这个方法把传进来的参数初始化,把之前“com.android.server.SystemServer”参数反射其main方法。
public static void main(String[] args) {
new SystemServer().run();
}
这里初始化“android_servers”的so库,里面包含了大量的Android的native需要加载的native服务,以及我们经常会用到各种服务(Display,powermanager,alarm,inputmanager,AMS,WMS)。创建了我们熟悉的SystemServerManger,为这个进程创建了Looper。
runSelectLoop启动一个Zygote的循环。
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
//sServerSocket中文件描述符的集合
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
//等待事件的循环
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
//每一次执行完了动作之后,等到又新的进程进来等待唤醒
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//执行的核心
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
先介绍下面两个队列
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
1.fds 文件描述符的队列.一般情况下,这个队列从头到尾只有一个,保证了当前只有一个Zygote在等待外面的监听。
2.peers ZygoteConnection队列。这个队列决定还有多少个socket事件没有处理。
这是构建的一段死循环。可以说是系统主Loop,就像iOS,ActivityThread中的Looper,用来响应外界的事件。
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
//每一次执行完了动作之后,等到又新的进程进来等待唤醒
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//执行的核心
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
这里面简单的逻辑如下,一旦有socket中监听到了数据过来,将会执行runOnce()方法,接着把对应的socket等待处理事件以及,对应的socket文件符移除掉。此时事件队列为0,这个循环再一次把Zygote监听添加回来。
runOnce
我们看看runOnce方法
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(
new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
//读取socket数据
parsedArgs = new Arguments(args);
if (parsedArgs.abiListQuery) {
return handleAbiListQuery();
}
...
fd = null;
//fork子进程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
//处理子进程的后续
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
runOnce做的事情分为三部分:
-
- 调用readArgumentList读取socket传过来的数据
-
- 通过 forkAndSpecialize fork新进程
-
- 处理fork出来的子进程
handleParentProc
private boolean handleParentProc(int pid,
FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
if (pid > 0) {
setChildPgid(pid);
}
if (descriptors != null) {
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
}
boolean usingWrapper = false;
if (pipeFd != null && pid > 0) {
DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
int innerPid = -1;
try {
innerPid = is.readInt();
} catch (IOException ex) {
Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
} finally {
try {
is.close();
} catch (IOException ex) {
}
}
...
try {
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
Log.e(TAG, "Error writing to command socket", ex);
return true;
}
return false;
}
子进程关闭fd入口,同时设置好pid,并且把pid带给fork出来的新进程。
这里按照惯例,我们画一幅时序图,总结
###Zygote 进程间通信原理
不熟悉Linux编程的同学看到死循环最后这一段,可能就有点懵。这里我解释一遍,在构造一下整个流程以及模型估计就能明白了。
虽然是socket通信,但是实际上和我们常说Java的socket编程稍微有一点点不一样。实际上更加像驱动中的文件描述的监听。这里和Android4.4的有点不一样,但是思路是一样。
Zygote监听服务端
从上面的代码,根据我的理论,peers这个ZygoteConnection是一个Zygote的链接对象,用来处理从远端的socket过来的消息。这个是一个关键类。我们看看这个ZygoteConnection究竟是怎么构造的。
private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
实际上此处会new一个ZygoteConnection,会把LocalServerSocket的accpet传进去。此时就和普通的socket一样进入阻塞。
让我先把LocalSocket这一系列的UML图放出来就能明白,这几个类之间关系。
实际上,所有的LocalSocket,无论是服务端LocalServerSocket还是客户端LocalSocket都是通过LocalServerImpl实现的。
protected void accept(LocalSocketImpl s) throws IOException {
if (fd == null) {
throw new IOException("socket not created");
}
try {
s.fd = Os.accept(fd, null /* address */);
s.mFdCreatedInternally = true;
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}
这个Os对象通过Libcore.os.accept(fd, peerAddress);调用native层。
文件:/libcore/luni/src/main/native/libcore_io_Posix.cpp
static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
sockaddr_storage ss;
socklen_t sl = sizeof(ss);
memset(&ss, 0, sizeof(ss));
//判断java层的socket对象是否为NULL
sockaddr* peer = (javaSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
socklen_t* peerLength = (javaSocketAddress != NULL) ? &sl : 0;
//核心,此处等待阻塞线程
jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength);
if (clientFd == -1 || !fillSocketAddress(env, javaSocketAddress, ss, *peerLength)) {
close(clientFd);
return NULL;
}
//一旦socket回调之后,将会通过底层的fd对象转化为java对象
return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL;
}
此处分为三步:
- 第一步,通过解析address是否为空,来决定阻塞的等待时长,此时传下来为null,为无限期的等待。
- 第二步,核心方法,通过define声明的NET_FAILURE_RETRY代码段,阻塞线程
- 第三步,一旦等待的socket链接有数据回调进来,则转化为java层的fd返回。
此处是阻塞的核心代码
#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
return_type _rc = -1; \
int _syscallErrno; \
do { \
bool _wasSignaled; \
{ \
//转化java的fd,对Java进行监听
int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
AsynchronousCloseMonitor _monitor(_fd); \
_rc = syscall_name(_fd, __VA_ARGS__); \
_syscallErrno = errno; \
_wasSignaled = _monitor.wasSignaled(); \
} \
if (_wasSignaled) { \
jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
_rc = -1; \
break; \
} \
if (_rc == -1 && _syscallErrno != EINTR) { \
/* TODO: with a format string we could show the arguments too, like strace(1). */ \
throwErrnoException(jni_env, # syscall_name); \
break; \
} \
} while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \
if (_rc == -1) { \
/* If the syscall failed, re-set errno: throwing an exception might have modified it. */ \
errno = _syscallErrno; \
} \
_rc; })
这里稍微解释一下,这段阻塞的核心方法的意思。
这循环代码的跳出条件有三个:
-
_wasSignaled 为true 也就是说此时AsynchronousCloseMonitor通过线程锁ScopeThreadMutex上锁的线程被唤醒,说明了该socket断开,也就断开了阻塞。
-
_rc 为-1 以及 _syscallErrno 错误标示位不为EINTER。rc为syscall_name(此时传进来的是socket的accept方法)。也就是说当accept链接出现异常的时候(返回-1)会一直在循环里面等待,除非为全局错误_syscallErrno 不是系统抛出的中断,则抛出异常。
-
当_rc不为-1,也就是说socket链接成功。则继续向下走。
因此从这里可以知道,Zygote在初始化runSelectLoop的时候,一开始会加入一个ZygoteConnection用于阻塞监听。一旦有链接进来,则唤醒则加入到peers队列中。在死循环下一个轮回的时候,通过执行runOnce执行fork新的进程。
虽然到这里似乎就完成整个流程了。但是实际上,google工程师写代码才不会这么简单就完成,而是做了一定的优化。
如果用Linux c写过服务器的哥们,就会明白这样不断的阻塞只会不断的消耗的cpu的资源,并不是很好的选择。
因此,runSelectLoop才有这一段代码
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
根据这段代码,从表面上可以清楚的知道,一开始把描述符都设置进去StructPollfd等长数组中。把这个数组交给Os.poll中。
我们先看看StructPollfd这个类是个什么存在。
文件/libcore/luni/src/main/java/android/system/StructPollfd.java
public final class StructPollfd {
/** The file descriptor to poll. */
public FileDescriptor fd;
/**
* The events we're interested in. POLLIN corresponds to being in select(2)'s read fd set,
* POLLOUT to the write fd set.
*/
public short events;
/** The events that actually happened. */
public short revents;
/**
* A non-standard extension that lets callers conveniently map back to the object
* their fd belongs to. This is used by Selector, for example, to associate each
* FileDescriptor with the corresponding SelectionKey.
*/
public Object userData;
@Override public String toString() {
return Objects.toString(this);
}
}
这个类十分简单。里面只有那么3个参数,events,revents,fd.分别是做什么的呢?
我们直接看看Os.poll方法底层的实现
文件:/libcore/luni/src/main/native/libcore_io_Posix.cpp
static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {
//反射获取structPollfd.java属性的属性id
static jfieldID fdFid = env->GetFieldID(JniConstants::structPollfdClass, "fd", "Ljava/io/FileDescriptor;");
static jfieldID eventsFid = env->GetFieldID(JniConstants::structPollfdClass, "events", "S");
static jfieldID reventsFid = env->GetFieldID(JniConstants::structPollfdClass, "revents", "S");
//转化为ndk底层的文件描述符
// Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[].
size_t arrayLength = env->GetArrayLength(javaStructs);
std::unique_ptr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength);
size_t count = 0; // Some trailing array elements may be irrelevant. (See below.)
for (size_t i = 0; i < arrayLength; ++i) {
ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
if (javaStruct.get() == NULL) {
break; // We allow trailing nulls in the array for caller convenience.
}
ScopedLocalRef<jobject> javaFd(env, env->GetObjectField(javaStruct.get(), fdFid));
if (javaFd.get() == NULL) {
break; // We also allow callers to just clear the fd field (this is what Selector does).
}
fds[count].fd = jniGetFDFromFileDescriptor(env, javaFd.get());
fds[count].events = env->GetShortField(javaStruct.get(), eventsFid);
++count;
}
std::vector<AsynchronousCloseMonitor*> monitors;
for (size_t i = 0; i < count; ++i) {
monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
}
//循环监听
int rc;
while (true) {
timespec before;
clock_gettime(CLOCK_MONOTONIC, &before);
//poll 阻塞进程
rc = poll(fds.get(), count, timeoutMs);
if (rc >= 0 || errno != EINTR) {
break;
}
// We got EINTR. Work out how much of the original timeout is still left.
if (timeoutMs > 0) {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
timespec diff;
diff.tv_sec = now.tv_sec - before.tv_sec;
diff.tv_nsec = now.tv_nsec - before.tv_nsec;
if (diff.tv_nsec < 0) {
--diff.tv_sec;
diff.tv_nsec += 1000000000;
}
jint diffMs = diff.tv_sec * 1000 + diff.tv_nsec / 1000000;
if (diffMs >= timeoutMs) {
rc = 0; // We have less than 1ms left anyway, so just time out.
break;
}
timeoutMs -= diffMs;
}
}
for (size_t i = 0; i < monitors.size(); ++i) {
delete monitors[i];
}
if (rc == -1) {
throwErrnoException(env, "poll");
return -1;
}
//唤醒之后更新runSelectLooper中的revents标识位,revents
// Update the revents fields in the Java android.system.StructPollfd[].
for (size_t i = 0; i < count; ++i) {
ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
if (javaStruct.get() == NULL) {
return -1;
}
env->SetShortField(javaStruct.get(), reventsFid, fds[i].revents);
}
return rc;
}
这个代码做了三件事情:
- 1.通过反射获取structPollfd.java中fd属性,revents,events属性。把这些参数设置到pollfd[] fds队列中。
- 2.把fds设置到poll进行监听
- 3.更新java层的structPollfd队列。
核心是第二步骤,linux的poll的函数。
而poll函数的作用就是如果没有检测到文件描述符的变化,则进程进入到睡眠状态,等到有人唤醒。由于此时传入的timeout为0,则不设置超时等待时间。
那么我们可以清楚的知道了,structPollfd做三个属性是什么。
-
第一个文件描述符,用来poll监听该文件描述符是否出现了变化。在这里还记得,传入的是zygote的socket文件吗?也就是说此时poll在监听socket是否出现了变化。
-
第二个event,作为pollfd中事件掩码的参数
-
第三个revent,代表了该文件描述符是否产生了变化。
因此,在每一次调用完Os.poll之后,如果socket有唤醒之后,会更新StructPollfd中的数据,也就有了下面这段判断逻辑
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
...
}
唤醒之后直接循环pollFds中,判断revents是否有变化,和POLLIN(实际上是0)相于不为0则表示socket文件变化了,才有下面的加入peers列表以及通过runOnce启动进程。
通过这样的优化,就能做到,当没有socket接入的时候,进程休眠,腾出了cpu资源。当socket接入,则唤醒进程,进入到accept,等待数据的接入。这样就能大大的提升了其中的资源利用率。(一些普通的web服务器也是如此的设计的)
这里只是解释了LocalSocket的服务端。
Zygote 客户端
实际上一般的ZygoteSocket的客户端,一般为SystemServer中的ActivitymanagerService.
我们看看在Android 7.0中当不存在对应的应用进程时候,会调用startProcessLocked方法中Process的start方法。
最终会调用
public static final String ZYGOTE_SOCKET = "zygote";
private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
这里的核心会调用一次ZygoteState的connect方法。
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED));
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
} catch (IOException ex) {
try {
zygoteSocket.close();
} catch (IOException ignore) {
}
throw ex;
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
此时会尝试的通过zygoteSocket也就是LocalSocket 去连接名为zygote的socket。也就是我们最开始初始化的在ZygoteInit中registerZygoteSocket的socket名字。
调用connect方法,唤醒Os.poll方法之后,再唤醒LocalServerSocket.accept方法,在循环的下一个,调用runOnce。
那么zygote又是怎么启动ActivityThread,这个应用第一个启动的类呢?
第一次看runOnce代码的老哥可能会被这一行蒙蔽了:
ZygoteConnection newPeer = acceptCommandPeer(abiList);
实际上在ZygoteConnection中,这个abiList不起任何作用。真正起作用的是ZygoteConnection.runOnce中readArgumentList
方法。
文件/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private String[] readArgumentList()
throws IOException {
/**
* See android.os.Process.zygoteSendArgsAndGetPid()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure.
*/
int argc;
try {
String s = mSocketReader.readLine();
if (s == null) {
// EOF reached.
return null;
}
argc = Integer.parseInt(s);
} catch (NumberFormatException ex) {
Log.e(TAG, "invalid Zygote wire format: non-int at argc");
throw new IOException("invalid wire format");
}
// See bug 1092107: large argc can be used for a DOS attack
if (argc > MAX_ZYGOTE_ARGC) {
throw new IOException("max arg count exceeded");
}
String[] result = new String[argc];
for (int i = 0; i < argc; i++) {
result[i] = mSocketReader.readLine();
if (result[i] == null) {
// We got an unexpected EOF.
throw new IOException("truncated request");
}
}
return result;
}
看吧实际上所有的字符串都是通过zygote的SocketReader读取出来,再赋值给上层。进行fork出新的进程。
在ActivityManagerSerevice的startProcessLocked
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
第一个参数就是ActivityThread,通过start方法,来打runOnce之后,进去handleChildProc,把ActivityThread的main反射出来,开始了Activity的初始化。
至此,从Linux内核启动到应用的AcivityThread的大体流程就完成了。
优化与思考
Android系统这么写Zygote孵化流程真的最佳的吗?辉哥曾经提问过一个问题,framework的启动流程该怎么优化。
我们去翻翻4.4的整个流程和android 7.0做对比。发现除了加载虚拟机是从art变成dvm之外,其他逻辑大体上一致。
唯一不同的就是runSelectLoop方法出现了变化。
android 4.4.4
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static final int GC_LOOP_COUNT = 10;
private static void runSelectLoop() throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
/*
* Call gc() before we block in select().
* It's work that has to be done anyway, and it's better
* to avoid making every child do it. It will also
* madvise() any free memory as a side-effect.
*
* Don't call it every time, because walking the entire
* heap is a lot of overhead to free a few hundred bytes.
*/
//做一次gc为了给每个子进程腾出内存空间
if (loopCount <= 0) {
gc();
loopCount = GC_LOOP_COUNT;
} else {
loopCount--;
}
//每一次通过select检测array中的fd有什么变化。
try {
fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}
//下面的逻辑一样和之前的一样
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}
这里稍微解释一下,在低版本fds和peers的意义还是没有多少变动,多了一个限制一次性最多也就4个ZygoteConnection监听。主要去看看下面的死循环之前的操作。
try {
fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}
这里面的代码实际上和上面的Os.poll那一段的类似。是为了监听socket中哪些出现了变化,而后唤醒进程。
这个方法直接调用的是native方法。
static jint com_android_internal_os_ZygoteInit_selectReadable (
JNIEnv *env, jobject clazz, jobjectArray fds)
{
...
FD_ZERO(&fdset);
//获取ndk层的fd
int nfds = 0;
for (jsize i = 0; i < length; i++) {
jobject fdObj = env->GetObjectArrayElement(fds, i);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
if (fdObj == NULL) {
continue;
}
int fd = jniGetFDFromFileDescriptor(env, fdObj);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
FD_SET(fd, &fdset);
if (fd >= nfds) {
nfds = fd + 1;
}
}
//select死循环阻塞
int err;
do {
err = select (nfds, &fdset, NULL, NULL, NULL);
} while (err < 0 && errno == EINTR);
if (err < 0) {
jniThrowIOException(env, errno);
return -1;
}
//查看哪些fd出现了变化,把index回调上去
for (jsize i = 0; i < length; i++) {
jobject fdObj = env->GetObjectArrayElement(fds, i);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
if (fdObj == NULL) {
continue;
}
int fd = jniGetFDFromFileDescriptor(env, fdObj);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
if (FD_ISSET(fd, &fdset)) {
return (jint)i;
}
}
return -1;
}
这个函数分为三个部分:
- 1.从java层获取fd的对象,通过jniGetFDFromFileDescriptor转化为具体的fd。每一次都加一个一,为select函数做准备。
- 2.调用select,监听所有的文件描述符中的变化
- 3.寻找变化的文件描述符(socket)对应的index,唤醒并且接受socket。
如果不太懂Linux api select函数,这里放出一个写select的比较好的博文:
https://www.cnblogs.com/skyfsm/p/7079458.html
这里简单的解释一下,select的参数。第一个参数,代表了有多少文件描述符加入了,此时只有一个,第二个参数,把fd每个参数对应的标志位,一旦这个标志位出现了变动,则代表这个文件描述符出现变化,socket接入了。其他先可以不管。
因此在最下面的那一段函数中,通过FD_ISSET的方法,判断变动的标志位,找到对应的fd,把对应的index返回。
这样就能正确找到哪个socket。并且处理对应的ZygoteConnection。
上个图总结:
思考
经过两者的比较,为什么在4.4.4版本使用select()去做,而到了7.0版本使用了poll。为什么这么做?先说说两个函数之间的区别。
简单的说,select和poll本质上都是对文件描述符的集合进行轮询查找,哪些socket出现了变化并且告诉Zygote。然而api的不同导致两者之间的策略不一样。
在4.4时代,大部分的手机内存吃紧(这一点从runLoop每隔10次就要gc一次就知道了),而select的好处就是每一次轮询都是直接修正每一个fd对应的标志位,速度较快。缺点是,一段标志位使用过每一个位上的0或者1来判断,也就限制了最大连接数量。
而7.0时代,大部分手机的性能变得比较好了。资源不再吃紧了,此时更换为poll函数。该函数的作用和select很相似。不过每一次轮询fd,都要修改pollfd结构体内部的标志位。这样就脱离标志位的限制了。
所以说,对于不同的api的,没有最好,只有最适用。
愚见
难道没办法,更好的办法吗?有!这只是个人看法,还记得前几年流行的ngnx吗?这个的底层是用epoll来实现的。
这种实现和单一的阻塞不一样。而是异步的IO。这方法只有Linux 2.6才开始支持。这个方法相比于select和poll。不是简单的轮询,因为当量级到了一定的时候,轮询的速度必定慢下来。而是通过回调的机制去处理。每一次通过内存映射的方式查找对应的fd,并且回调。这样就省去了内存在调用fd时候造成的拷贝(从内核空间到用户空间)。
其次,epoll这个函数没有数量的限制,而是由一个文件描述符去控制所有的文件描述符。
基于这两个理由,很明显epoll才是最佳的选择。
但是,最佳就必须选择吗?不,我们只选择了最合适的。我刚才看了下android 9.0的源码。发现还是继续使用poll机制。对于android来说zygote诞生出新的进程的情况不多见,量级远没有达到服务器的地步,加上使用epoll,下面的fork的机制可能变动大,没有选择也是情理之中。
当然,如果有哥们看过Handler的源码,就知道Handler有一层ndk层,下层也是用epoll做等待死循环处理。有机会再源码解析解析。
总结
实际上最后这一段Zygote孵化原理,我发现老罗的书,还有网上的资料都说不详细,但是这却是最重要的一环,是Zygote沟通应用程序的核心代码。特此在此记录一下。
那么Zygote诞生做了什么?在Activity启动前的角色是什么?现在就明白了。
-
1.Zygote是init进程之后第一个诞生出来的孵化进程。就以Android系统的framework来说,Zygote是Android系统一切进程的母亲。
-
2.Zygote第一个孵化的进程是SystemServer进程。
-
3.初始化虚拟机是通过jniInvoaction,加载对应的so库
-
4.SystemServer进程初始化,AMS,WMS,PMS,DisplayManager(显示),InputManager(键盘),PowerManager(电源)…
-
5.Zygote 诞生新的进程都是通过fork诞生的。
-
6.Zygote 开启socket监听死循环,在低版本使用select来阻塞,高版本使用poll来阻塞。
参考资料:
https://segmentfault.com/a/1190000003063859?utm_source=tag-newest
https://www.cnblogs.com/amanlikethis/p/6915485.html
题外话
写的比较粗浅,也不是很专业。看到错误可以找我纠正。估计很多人都懂这些了,更多的只是把这两年学习的复习和整理。
可以来这个链接下来找我:https://blog.****.net/yujunyu12/article/details/87918948
简书居然不允许发表字数多一点的文章,看情况,可能舍弃掉简书了