Framework - Zygote

参考文章1参考文章2

一、概念

Zygote是 Android 中的第一个进程,负责孵化(fork)其它进程,而它自己由 Linux 内核启动的用户级进程 Init 创建。

二、作用

        应用程序不能直接以本地进程的形态运行,必须在一个独立的虚拟机中运行,一些每个APP都会用到系统资源,如果每次都新启动一个虚拟机进行加载,将严重拖慢应用程序的启动速度。

        Linux 中 fork 出的子进程与父进程共享内存资源(子进程能访问父进程资源),只有当子进程改写内存时,操作系统才会为其分配一个新页面,并将老页面上的数据复制一份到新页面,这就是写时拷贝机制(Copy On Write,fork操作不会将进程中所有线程拷贝,只会拷贝当前线程)。

        Zygote进程在初始化时会创建虚拟机并加载一些APP都会用到的系统资源,这样 fork 出的子进程能共享这些资源,接下来只需装载 apk 文件的字节码就可以运行应用程序了,可以大大缩短应用的启动时间。

三、启动过程

在这里插入图片描述

3.1 init 进程

        当系统启动时,init 进程是继 Linux 内核启动后第二个启动的进程,它是在用户空间被创建的进程。可以通过命令 adb shell ps 查看 init 进程的 PID(PID 是当前进程的 id,值越小越先启动,PPID 是父进程的 id), Linux内核PID=0,init的PID=1。

        用来启动和守护系统关键服务,PPID=1的进程就是,被杀死会重启手机(所以要实现一个系统服务又不想被杀死,最好的方式就是让服务由 init 进程启动成为关键服务)。 

在这里插入图片描述​​

3.1.1 init.cpp

init进程也是一段可执行的代码,位于 system/core/init/init.cpp。

int main(int argc, char** argv) {
    //创建文件并挂载
    if (is_first_stage) {
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }
    //初始化属性相关资源
    property_init();
    //启动属性服务
    start_property_service();
    Parser& parser = Parser::GetInstance();
    //解析init.rc配置文件
    parser.ParseConfig("/init.rc");

}

3.1.2 init.rc

        由Android初始化语言(Android Init Language)编写的脚本,由 Action、Command、Service、Option组成,位于system/core/rootdir/init.rc。

        Zygote由 init 进程解析 init.rc 脚本以 service 服务的形式启动的。其中的一段 import /init.${ro.zygote}.rc 就是启动 Zygote 的脚本,会根据系统属性 ro.zygote 的具体值加载对应的脚本(有32、64、32_64、64_32四种)。

Action 决定何时执行相应的Service。
Command 启动停止服务。
Service 启动时都会 fork 成一个子进程。
Option 是Service的可选项,配合使用。
 import /init.${ro.zygote}.rc

3.1.3 init.zygote**.rc

例如在 system/core/rootdir/init.zygote32.rc 脚本中,第一行是启动一个进程名字是zygote,会执行 /system/bin/app_process,而 -Xzygote /system/bin --zygote --start-system-server 是入口程序传入的参数。

//启动Ztgote进程
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
//启动ServiceManager进程
service servicemanager /system/bin/servicemanager

3.2 Native层

3.2.1 app_main

例如在 init.zygote32.rc 脚本中 /system/bin/app_process 最终会执行 frameworks/base/cmds/app_process/app_main.cpp 的 mian() 函数,这里会将最初名字“app_process”(这个名字是在Android.mk文件中指定的)改成“zygote”,通过 AppRuntime.start() 启动Zygote。

int main(int argc, char* const argv[]) {
    //创建AppRuntime对象,它继承自AndroidRuntime
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    //解析init.rc中的参数(-Xzygote /system/bin --zygote --start-system-server)
    while(i < argc) {...}
    //app_process改名zygote
    runtime.setArgv0(niceName.string(), true /* setProcName */);
    //调用start()启动Zygote
    if(zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    }
}

3.2.2 AppRuntime(AndroidRuntime)

AppRuntime 继承自 AndroidRuntime,位于 frameworks/base/core/jni/AndroidRuntime.cpp。

  • 创建虚拟机:startVm() 创建虚拟机,startVm() 有很多虚拟机的参数配置,比如堆内存大小(如果是系统工程师,内存调优和其他虚拟机的调优就是在这个函数处理)。
  • 注册Android的JNI函数:startReg() 注册虚拟机中的 JNI 方法,我们写 Java 代码能调用 Native(C/C++ 函数),就是因为在这里做了注册处理了映射关系。
  • 进入到Java层:反射找到 ZygoteInit 的 main() 方法,通过 JNI 调用进入到 Java 层。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);    //加载libart.so
    JNIEnv* env;
    //启动虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);
	//注册虚拟机中的JNI方法
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    //进入Java层
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V"); //找到ZygoteInit的main()方法
    if (startMeth == NULL) {
		ALOGE("JavaVM unable to find main() in '%s'\n", className);
	} else {
		env->CallStaticVoidMethod(startClass, startMeth, strArray); //JNI调用JAVA方法,执行main()
	}
}

3.3 Java层(ZygoteInit

位于 /frameworks/base/core/java/com.android.internal.os.ZygoteInit.java。

// argv 就是 init.{ro.zygote}.rc 脚本写的参数(-Xzygote /system/bin --zygote --start-system-server)
public static void main(String argv[]) {
	// 创建 Server 端的 Socket 用于 Zygote 通信
	ZygoteServer zygoteServer = new ZygoteServer();
	zygoteServer.registerServerSocketFromEnv(socketName); 
	// 加载系统类、系统资源等
	if (!enableLazyPreload) {
		preload(bootTimingsTraceLog);
	}
	// 创建 SystemServer 用于 PMS、AMS 等系统服务
	if (startSystemServer) {    //是否启动SystemServer进程,来自init.rc中的参数--start-system-server
		Runnable r = forkSystemServer(abiList, socketName, zygoteServer); //创建SystemServer进程
        if (r != null) { //不为null说明是SystemServer进程
            r.run();    //进而调用 SystemServer 的 main() 方法跑起来(开启系统服务)
            return;    //结束函数与Zygote进程脱离
        }
        //为null说明是Zygote进程,接着往下走
	}
    //开启循环等待 Client 端(即AMS)发来的 fork 应用进程请求
	zygoteServer.runSelectLoop(abiList);
}

3.3.1 启动ZygoteServer建立Socket通信

创建一个 Server 端的 Socket 并开启循环等待 Client 端发来的请求,一旦有新进程需要 AMS 就通过这个 socket 跟 zygote 通信。

3.3.2  预加载类和资源

系统类文件(system/etc/preloaded-classes,由framework/base/tools/preload工具判断的加载时间超过1250微秒的类,非常多是Android系统启动慢的原因之一)、Resource资源、SharedLibraries共享库资源、OpenGL、TextResources文字资源......一般都是只读的不会进行修改,而且是很多APP都可能会用到的,因此预先加载后,通过 Zygote 孵化出的进程共享虚拟机内存和框架层资源,这样大幅度提高应用程序的启动和运行速度。(类加载机制:加载子类都会先加载父类。被大量APP继承的基类就不会每次都要加载如AppCompatActivity)。

static void preload(TimingsTraceLog bootTimingsTraceLog) {
    preloadClasses(); //预加载并初始化 /system/etc/preloaded-classes 中的类
    preloadResources(); //预加载系统资源
    preloadOpenGL(); //预加载 OpenGL
    preloadSharedLibraries(); //预加载 共享库,包括 android、compiler_rt、jnigraphics 这三个库
    preloadTextResources(); //预加载文字资源
    WebViewFactory.prepareWebViewInZygote(); //WebViewFactory中一些必须在 zygote 进程中进行的初始化工作,用于共享内存
}

3.3.3 启动SystemServer用于系统服务

详见SystemServer文章

是 Zygote 的第一个子进程,用于系统服务。位于 frameworks/base/services/java/com.android.server.SystemServer.java。

private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
    //启动 SystemServer 的参数
    String args[] = {
        "--setuid=1000",
        "--setgid=1000",
        "--nice-name=system_server",    //进程名称“system_server”
        "com.android.server.SystemServer" //全类名路径
    };
    //创建一个子进程,也就是 SystemServer 进程
    pid = Zygote.forkSystemServer(...);
    //如果当前代码运行在子进程中 ,也就是 SystemServer 进程中
    if (pid == 0) {
        if (hasSecondZygote(abiList)) { //如果有第二个 Zygote 进程,即
            waitForSecondaryZygote(socketName);
        }
        zygoteServer.closeServerSocket(); //关闭并释放从 Zygote 进程复制过来的 socket
        return handleSystemServerProcess(parsedArgs); //完成创建 SystemServer 进程的剩余工作
    }
    //否则是zygote进程,返回null
    return null;
}
private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
    //设置当前进程名为 "system_server"
    if (parsedArgs.niceName != null) { 
        Process.setArgV0(parsedArgs.niceName);
    }
    //dex优化
    if (systemServerClasspath != null) {
        performSystemServerDexOpt(systemServerClasspath);
    }
    //创建PathClassloader类加载器
    if (systemServerClasspath != null) {
        cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
        Thread.currentThread().setContextClassLoader(cl);
    }
    //继续处理剩余参数
    return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    //重定向 System.out 和 System.err 到 Android log 输出
    RuntimeInit.redirectLogStreams();
    //一些初始化工作(设置默认的未捕捉异常处理方法, SystemServer中线程异常的时候,一般是释放资源杀当前进程)
    RuntimeInit.commonInit();
    //native层初始化(开启Binder线程池供SystemServer和其它进程通信)
    ZygoteInit.nativeZygoteInit();
    //调用入口函数初始化SystemServer自身
    return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
protected static Runnable applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    final Arguments args = new Arguments(argv); // 解析参数
    //通过反射调用 com.android.server.SystemServer 的 main() 方法
    //返回一个 Runnable,最终在 Zygote 的 main() 方法中执行器 run() 方法(老版本是抛异常在 Zygote的main()中捕获处理)
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

​​

 三、通讯机制 Socket

进程间通信是一对多模型,支持C/S(Client-Server)架构的只有 Socket 和 Binder,而采用 Socket 而不是 Binder 是因为:

  • Binder 机制复杂:首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和SystemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事。
  • 隔离:如果zygote启用binder机制,再fork出SystemServer,那么SystemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。
  • 性能问题:开进程是一个效率低的事情(处理慢),而Binder是一个传输效率高的机制(请求快),假如一下子开10个进程会导致请求都堵在那里,从200M飙到2G打乱系统内存分配。
  • 死锁:假设是 AMS 使用 Binder 通信告知 Zygote fork 一个 app 进程,为了保证不会出现并发问题,AMS 和 Zygote 的通信会加锁,AMS 要和 Zygote 通信拿的 Binder 是属于 Zygote 的(获取的要通信方的 Binder 代理),此时 Zygote fork 了进程,会连带把 Binder 等待锁的状态也复制过去,那么子进程的 Binder 加了锁由谁来解锁?子进程没有解锁,就会出现死锁。
  • 读写错误:要 new 一个 ProcessState 用于 Binder 通信时,需要 mmap 申请一片内存用以提供给内核进行数据交换使用。而如果直接 fork 了的话,子进程在进行 binder 通信时,内核还是会继续使用父进程申请的地址写数据,而此时会触发子进程 COW(Copy on Write),从而导致地址空间已经重新映射,而子进程还尝试访问之前父进程 mmap 的地址,会导致 SIGSEGV、SEGV_MAPERR段错误。

猜你喜欢

转载自blog.csdn.net/HugMua/article/details/131199237