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

基于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系统进程Zygote启动过程的源代码分析:https://blog.csdn.net/Luoshengyang/article/details/6768304
Android应用程序进程启动过程的源代码分析:https://blog.csdn.net/Luoshengyang/article/details/6747696
Dalvik虚拟机的启动过程分析:https://blog.csdn.net/Luoshengyang/article/details/8885792

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容