android进程创建及android环境的准备

·  阅读 199

基于Android 9.0的源码浅析Zygote进程的创建过程,android应用进程的过程,和系统为应用进程准备android环境的过程。

Zygote进程创建

在android中所有的应用进程和系统进程SystemServer都是由Zygote进程fork出来的,先看看zygote进程为分析应用进程创建做好准备

在native层,init进程通过解析init.zygote32.rc / init.zygote64.rc文件后创建Zygote进程,下面是它的代码执行入口
app_main.cpp

int main(int argc, char* const argv[])
{
  
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
   
    // 配置虚拟机参数
    // ...
 
    // 参数解析
    // ...

    if (zygote) {
         //一、AndroidRuntime.start 启动虚拟机
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } 
}
复制代码

AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    //适配层JniInvocation在旧版4.4 可以根据系统属性选择是加载Dalvik虚拟机还是ART运行时
    //https://blog.csdn.net/Luoshengyang/article/details/18006645
    JniInvocation jni_invocation;
   //加载对应虚拟机的so
    jni_invocation.Init(NULL);
    JNIEnv* env;

    //解析设置的虚拟机参数并初始化虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
   //1、设置线程创建方法为javaCreateThreadEtc(当native的线程需要使用jni时,这个方法会
   // 在创建线程时将jni上下文环境与线程绑定,因而可以执行Java代码)
   //2、注册系统类的jni方法
    if (startReg(env) < 0) {
        return;
    }

    //"com.android.internal.os.ZygoteInit" -》"com/android/internal/os/ZygoteInit"
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
    // 调用ZygoteInit.main()
    env->CallStaticVoidMethod(startClass, startMeth, strArray);

    mJavaVM->DetachCurrentThread() ;
    mJavaVM->DestroyJavaVM();

}

复制代码

从native层调用java层ZygoteInit的main方法
ZygoteInit

public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();
        final Runnable caller;
        try {
            //二、通过环境变量获取在native创建好的socket描述符(socket的类型为unix domain socket,
            //地址是/dev/socket/zygote)
            zygoteServer.registerServerSocket(socketName);
            if (!enableLazyPreload) {
                 //三、预加载android进程通用的资源,类,共享库等
                preload(bootTimingsTraceLog); 
            }
            if (startSystemServer) {
                //四、启动SystemServer进程,启动android核心服务
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
                //fork之后父进程即zygote本身会返回null,而systemServer会返回runnable执行SystemServer.main()
                if (r != null) {
                    r.run();
                    return;
                }
            }
             //五、
            //通过pollI/O多路复用机制监听套接字连接和数据可读。当客户端建立了一个连接后,
           //调用accept获取已连接描述符添加到监听的数组。
           //当数据可读时则处理对端的数据(这里是创建新进程需要的参数)
           //并调用 Zygote.forkAndSpecialize创建新进程
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }
       //在上面runSelectLoop中如果收到systemServer进程创建新进程的请求,fork后子进程返回的
      //caller!=null。一般来说这里是执行ActivityThread.main。而zygote进程在runSelectLoop中继续监听socket描述符
        if (caller != null) {
            caller.run();
        }
    }
复制代码

Zygote 进程的五个关键步骤

一、在native(AndroidRuntime.cpp中的start方法)启动虚拟机并调用startReg()注册JNI函数,然后jni调用回java层的ZygoteInit的main方法来执行后续方法-----虚拟机这里可以展开
二、zygoteServer.registerServerSocket(socketName)通过环境变量获取在native创建好的socket描述符(socket的类型为unix domain socket,地址是/dev/socket/zygote)
三、preload()预加载通用类、drawable和color资源和共享库等。因为fork进程时是copy on write(只要没有进程试图写它自己的私有区域,它们就可以继续共享物理内存中对象的那一个副本),从而提高app启动效率,减少内存的消耗(从整个系统来看);
四、在ZygoteInit.forkSystemServer中fork SystemServer进程并调用ZygoteInit的handleSystemServerProcess进行初始化工作。其中最主要的是调用了ZygoteInit.nativeZygoteInit()进行Binder机制的初始化工作还有调用SystemServer的main方法来启动android中的核心服务PackageManagerService和ActivityManagerService等。
五、zygoteServer.runSelectLoop()监听fork子进程的socket请求

应用进程创建

当我们要启动四大组件,而组件所在的进程还没启动时,系统就会调用ActivityManagerService.startProcessLocked来启动进程。ActivityManagerService.startProcessLocked(这里有好几层省略了)->Process.start ->ZygoteProcess.start

ZygoteProcess.start

private Process.ProcessStartResult startViaZygote(final String processClass,
                                                      final String niceName,
                                                      final int uid, final int gid,
                                                      final int[] gids,
                                                      int debugFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      String seInfo,
                                                      String abi,
                                                      String instructionSet,
                                                      String appDataDir,
                                                      String invokeWith,
                                                      String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        ArrayList<String> argsForZygote = new ArrayList<String>();

        //省略的是添加传给应用进程的参数,比较熟悉的参数有uid,gid,targetSdkVersion等

        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
复制代码

接下来先看openZygoteSocketIfNeeded,它的关键语句是ZygoteState.connect(mSocket);其中mSocket的值为"zygote";
之后的zygoteSendArgsAndGetResult 就是往socket写入参数传给zygote。

public static ZygoteState connect(String socketAddress) throws IOException {
            DataInputStream zygoteInputStream = null;
            BufferedWriter zygoteWriter = null;
            final LocalSocket zygoteSocket = new LocalSocket();

            try {
                //zygoteSocket.connect 里面创建了socket和调用了connect
               //过程和我们网络通信时的socket一样,不过类型不同,
              //一个的网域是AF_INET传地址为ip地址,一个的网域为AF_UNIX传地址为文件名
                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;
            }
            //从zygote那边的服务端获取支持的ABI架构
            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                    Arrays.asList(abiListString.split(",")));
        }
复制代码

看完客户端的建立和连接,写入。再看看在zygote的socket服务端,在zygote创建的步骤二获取了在native创建的socket套接字(已经调用过socket()、bind()、listen()),在步骤五调用了runSelectLoop()来监听进程的创建,下面就看下具体的代码。
ZygoteServer.runSelectLoop

Runnable runSelectLoop(String abiList) {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        fds.add(mServerSocket.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 {
                //poll io复用,与普通的阻塞的io调用区别是可以等待多个io事件,任意一个可读时返回。
                //详细的可以看《UNIX网络编程卷1第三版》第六章
                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;
                }
                //第0个固定为监听套接字,当有新的连接准备好时通知poll,走到这里
                if (i == 0) {
                    //调用accept 获取已完成的连接
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    //将连接加入监听的列表,当有数据可读时再走else分支
                    fds.add(newPeer.getFileDesciptor());
                } else {
                   //现有连接有数据可读时通知poll,走到这个分支
                    try {
                        ZygoteConnection connection = peers.get(i);
                       //处理客户端传来的数据,fork在这里进行
                        final Runnable command = connection.processOneCommand(this);
                        if (mIsForkChild) {
                           //fork后的子进程的mIsForkChild 为true
                            return command;
                        } else {
                          //zygote进程走这个分支
                           //清理工作
                            if (connection.isClosedByPeer()) {
                                connection.closeSocket();
                                peers.remove(i);
                                fds.remove(i);
                            }
                        }
                    } catch (Exception e) {
                        if (!mIsForkChild) {
                           //清理工作
                            ZygoteConnection conn = peers.remove(i);
                            conn.closeSocket();
                            fds.remove(i);
                        } else {
                            Log.e(TAG, "Caught post-fork exception in child process.", e);
                            throw e;
                        }
                    }
                }
            }
        }
    }
复制代码

ZygoteConnection.processOneCommand处理客户端传来的参数

   Runnable processOneCommand(ZygoteServer zygoteServer) {
        String args[];
        Arguments parsedArgs = null;
        //读取socket传来的参数
        args = readArgumentList();
        //处理参数
        parsedArgs = new Arguments(args);
        //对应在客户端看到的getAbiList获取支持的ABI架构
        //写入支持的abi后return null,等到客户端再写入参数时,poll监听会再次进到这个方法
        if (parsedArgs.abiListQuery) {
            handleAbiListQuery();
            return null;
        }

        //省略一些参数的处理和文件描述符的处理

        //里面调用了fork系统调用来创建子进程
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                parsedArgs.appDataDir);

        if (pid == 0) {
             // 新创建的进程走这个分支
            // 设置mIsForkChild = true
            zygoteServer.setForkChild();
            //fork之后socket文件描述符共享了,而新进程并不需要,所以关闭。
            zygoteServer.closeServerSocket();
            return handleChildProc(parsedArgs, descriptors, childPipeFd);   
        } else {
            //zygote进程走这里,返回的pid为新进程的id,通过socket写入传给ams所在的客户端
            handleParentProc(pid, descriptors, serverPipeFd);
            return null;
        }
    }
复制代码

新的进程创建后主要逻辑在handleChildProc
ZygoteConnection.handleChildProc

private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
            FileDescriptor pipeFd) {
       
        //这里关闭的是与客户端连接的socket套接字,前面的zygoteServer.closeServerSocket();
        //关闭的是监听客户端连接建立的socket套接字。
        closeSocket();
        //处理标准输入、标准输出、标准错误的文件描述符
        if (descriptors != null) {
            try {
                Os.dup2(descriptors[0], STDIN_FILENO);
                Os.dup2(descriptors[1], STDOUT_FILENO);
                Os.dup2(descriptors[2], STDERR_FILENO);

                for (FileDescriptor fd: descriptors) {
                    IoUtils.closeQuietly(fd);
                }
            } catch (ErrnoException ex) {
                Log.e(TAG, "Error reopening stdio", ex);
            }
        }
        //设置进程名字
        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName);
        }
       //进程创建后的主要初始化工作
        return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
                    null /* classLoader */);
    }
复制代码

ZygoteInit.zygoteInit

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
        //重定向System.out 和System.err到android 的log
        RuntimeInit.redirectLogStreams();
       //设置一个默认的未截获异常处理器KillApplicationHandler( 把crash信息添加到DropBox,弹框退出还
       //是退出并报告。最终调用Process.killProcess杀死进程。ActivityThread.main会调用ams的attach方
       //法里面调用thread.asBinder().linkToDeath注册一个死亡回调,从而应用进程挂了之后ams可以调用
       //cleanUpApplicationRecordLocked清理应用的相关信息比如activity,service等信息)
        RuntimeInit.commonInit();
       // jni方法最终调用了app_main.cpp中的onZygoteInit(),代码下面两句
       // 打开binder驱动
       //sp<ProcessState> proc = ProcessState::self();
       // 启动binder的线程池 
      // proc->startThreadPool();
      //更多binder知识看老罗的binder系列https://blog.csdn.net/luoshengyang/article/details/6618363
        ZygoteInit.nativeZygoteInit();
        //解析argv获取className,反射允许main方法。如果是普通应用进程这里就是ActivityThread
       // 如果是systemServer进程就是SystemServer类
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }
复制代码

应用进程创建小结

ams通过socket向zygote进程发起fork请求,zyogte进程调用完fork后应用进程便创建好了,应用进程进行初始化工作(设置未截获异常处理器,打开binder驱动,启动binder线程池等),然后调用ActivityThread.main

android环境

在android中我们更多接触的是四大组件和Application,而它们都离不开Context,通过分析Context理解android环境

Context

Context作为android环境上下文提供了与android系统打交道的方法
而ContextImpl作为其实现类我们看下它的方法与关键成员变量,就能大概知道这个类的作用

四大组件相关,封装了ActivityManager.getService()的调用即请求ActivityManagerService的方法
bindServicexxx 
registerReceiverxxx
getContentResolver
startActivityxxx
sendBroadcastxxx
startServicexxx
操作SharedPreferences,实质是读写xml文件,简单的配置持久化时我们使用SharedPreferences
xxxSharedPreferences
创建Context
createxxxContext
检测权限,如果不是系统应用或者root,请求ActivityManagerService检测权限。
checkxxxPermission
获取系统服务,常见的getSystemService(Context.LAYOUT_INFLATER_SERVICE)
context.getSystemService(Context.CONNECTIVITY_SERVICE)等
getSystemService
获取Resources从而获取资源文件
getResources
复制代码

看一下关键的成员变量

//运行在应用进程主线程的对象,记录了应用当前运行的状态(各种组件的状态,application),
//application和四大组件生命周期的执行(与ActivityManagerService通信时传一个binder即
//ApplicationThread作为回调,然后执行相应组件的生命周期方法)
ActivityThread mMainThread
//apk的基本信息apk的文件路径等,AndroidManifest.xml上的信息(待调试截图)
LoadedApk mPackageInfo
//ClassLoader 通过加载dex字节码将目标类加载到内存,在android中ClassLoader一般为
//PathClassLoader,主要用于系统和app的类加载器,
//也可以使用DexClassLoader来加载系统没有安装的apk或者jar文件
ClassLoader mClassLoader
//AssetManager 默认会加载位于"/system/framework/framework-res.apk"的系统资源,
//然后再再加载应用的资源。资源的加载通过ApkAssets.loadFromPath 解析apk中的
//resources.arsc(apk编译打包的时候会给每一个非assets资源一个ID值存在R.java文件中,
//最后生成资源索引表resources.arsc,用来描述那些具有ID值的资源的配置信息)。
//当我们根据资源id获取资源时,从这个索引表根据id找到对应资源项再根据当前配置信息
//(dpi、语言等)选择最匹配的资源项
Resources mResources
复制代码

下面分析Activity和Application在创建时与Context的关系

Application创建
ActivityThread.handleBindApplication中调用LoadedApk的makeApplication创建Application

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        Application app = null;
        String appClass = mApplicationInfo.className;
        //如果在mainfest没有设置自定义的application就使用默认的
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }
        try {
            java.lang.ClassLoader cl = getClassLoader();
            //创建ContextImpl
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //反射实例化application或自定义的application,并调用attach方法
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
        }
       return app;
}
复制代码

Activity创建和Context的关系
ActivityThread.performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       //只保留创建Activity时和Context有关系的代码
       //调用ContextImpl的createActivityContext方法创建ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
       //反射实例化activity
        activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        if (activity != null) {
          activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
       }
}
复制代码

1、可以看到的是Activity和Application的创建过程都实例化了ContextImpl,只是调用的静态工厂方法不同,createAppContext和createActivityContext二者主要区别在于后者传入了activityToken和overrideConfiguration。(activityToken的主要作用是获取资源时和activityToken关联起来,overrideConfiguration的主要作用是使得某个activity可以有不同的配置信息,这样获取资源时根据这个配置信息来取。总的来说就是资源获取可以根据activity达到差异化的效果。)
2、Activity和Application都调用了attach方法最后都调用了ContextWrapper的attachBaseContext方法。两个类都是继承自ContextWrapper,顾名思义,Context和ContextImpl使用了装饰模式,ContextWrapper通过attachBaseContext持有ContextImpl,并且方法的实现都交给了ContextImpl,ContextWrapper子类通过重写方法增强功能。ContextThemeWrapper重写主题相关方法,Activity与UI密切相关,于是继承了ContextThemeWrapper,Application与UI关系不大所以直接继承ContextWrapper。

总结:

1、进程创建的过程(zygote的创建、ams通过socket请求zygote创建新进程),在zygote进程android部分比较耗时的类提前加载,部分系统资源提前加载,之后创建应用进程的时候,fork子进程不需要再次加载从而提高效率。向zygote请求后的进程初始化:普通应用进程创建后调用ActivityThread.main()作为入口,SystemServer进程入口为SystemServer.main。最后为新创建的进程打开binder驱动,启动binder线程池,后续就可以使用binder进行进程间的通信了。
2、Context(ContextImpl)作为android环境上下文提供给我们与四大组件交互的能力(封装对AMS的请求)、获取ClassLoader、获取apk的信息(通过LoadedApk)、获取资源(Resource)等等。application和四大组件的创建时都会创建ContextImpl或使用application的ContextImpl attach到组件对应的Context中去。


分类:
Android
标签:
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改