深入理解AMS之应用进程创建

概述

  在安卓系统中诸多应用程序都会在桌面应用Launcher中创建一个shortcut启动图标,当我们点击这个图标就可以进入其所指示的应用程序首页,这个过程是通过调用ActivityManagerService(以下简称AMS)的方法startActivity()完成的。当调用startActivity()后AMS会首先判断当前进程是否已经创建,如果没有创建则会进入应用进程的创建流程。从这期间涉及到的进程来看,大概流程如下:

  当然,上述AMS服务是寄生在SystemServer进程中(对AMS的启动过程不熟悉的同学可以参考深入理解AMS之启动过程这篇文章),此处为了直观描述,单独把AMS给拎出来了。

  从流程上来说,调用startActivity()到真正应用进程的页面显示过程经历了多个进程间的协作,甚至这些进程间的通信手段也不尽相同。

发起应用进程的创建

  AMS中startActivity()方法最终会调用到ActivityStaskSupervisor(以下简称ASS)的startSpecificActivityLocked()中,这个方法正是是否需要启动新进程来承载ActivityRecord的关键点。

// frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);
        r.task.stack.setLaunchTime(r);
        if (app != null && app.thread != null) {//如果当前要启动的Activity已经有承载进程
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
        }
        //执行到此处说明当前要启动的Activity的进程还未启动,需等待进程启动后再执行Activity操作。
        //此处调用startProcessLocked启动进程
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

  通过阅读上述代码我们可以看到当前ActivityRecord承载进程是否存在的判断逻辑是app.thread,app.thread是一个Binder,当ActivityThread(即应用进程)启动时会通过attachApplication()方法将这个binder传给AMS以作为后续AMS与ActivityThread的binder通信接口。在此处如果app.thread为空则说明对应的ActivityThread并未创建,此后即进入应用进程的创建流程。

  上述方法最后通过调用AMS.startProcessLocked()方法来创建了一个新进程。具体的调用时序图如下:

  在AMS中有多个重载的startProcessLocked()方法,但最终多调用到以下startProcessLocked()中。

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        //part1 清理环境
        if (app.pid > 0 && app.pid != MY_PID) {
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.remove(app.pid);//移除进程记录
                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);//移除对app进程的启动超时计算
            }
            app.setPid(0);//清空pid
        }
        mProcessesOnHold.remove(app);
        updateCpuStats();//更新cpu使用状态

        try {
            //part2 gid的获取
            int uid = app.uid;
            int[] gids = null;
            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
            if (!app.isolated) {
                int[] permGids = null;
                try {
                    final PackageManager pm = mContext.getPackageManager();
                    permGids = pm.getPackageGids(app.info.packageName);
                    //...
                } catch (PackageManager.NameNotFoundException e) {
                    Slog.w(TAG, "Unable to retrieve gids", e);
                }
                if (permGids == null) {
                    gids = new int[2];
                } else {
                    gids = new int[permGids.length + 2];
                    System.arraycopy(permGids, 0, gids, 2, permGids.length);
                }
                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
                gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
            }
            //...
            
            //part3 flags与abi的获取
            int debugFlags = 0;
            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
                debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
            }
            //...
            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
            if (requiredAbi == null) {
                requiredAbi = Build.SUPPORTED_ABIS[0];
            }
            //...
            boolean isActivityProcess = (entryPoint == null);
            //part4 启动进程
            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);//内部通过socket通知Zygote创建进程并返回结果
            //part5 进程启动后扫尾工作
            app.setPid(startResult.pid);
            app.usingWrapper = startResult.usingWrapper;
            app.removed = false;
            app.killed = false;
            app.killedByAm = false;
            synchronized (mPidsSelfLocked) {
                this.mPidsSelfLocked.put(startResult.pid, app);
                if (isActivityProcess) {
                    Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                    msg.obj = app;
                    mHandler.sendMessageDelayed(msg, startResult.usingWrapper
                            ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);//超时计算,超时时间10s
                }
            }
        } catch (RuntimeException e) {
            app.setPid(0);
            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
            if (app.isolated) {
                mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
            }
        }
    }

  从startProcessLocked()代码的内容来看,大概可以分为5个部分:

  1. 进程创建环境的清理。主要把当前app的进程记录及上次的启动超时、启动记录给删除掉,同时尝试更新cpu状态。
  2. gid的获取。uid与gid是linux常见的概念,主要指用户id与用户组id。
  3. debugFlags及abi的获取。debugFlags指的是当前进程所支持的调试模式,而abi则指当前进程所支持的cpu指令集架构,如果当前应用程序进程没有指明支持的abi,则使用当前系统所支持的abi。
  4. 启动进程。进程的启动是通过调用Process.start()方法完成的,此处需要注意的是entryPoint即启动类的指明,一般而言entryPoint都为空,即启动类是ActivityThread。
  5. 进程启动后的扫尾工作。进程启动的扫尾工作在此处分为两种:(一)、根据启动结果设置app进程id及进程状态,(二)、向AMS服务线程投送超时消息,如果10s内步骤4中被启动的应用进程没有通过attachApplication()返回启动结果,则此处投送的超时消息会触发执行并把app相关信息给移除。

  应用进程启动是通过Process.start()完成的,也就是上述的步骤4。这个方法的具体内容如下:

// frameworks/base/core/java/android/os/Process.java
    public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);//via经由
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }

  start via zygote英文含义是“通过zygote孵化器启动”,由此可见framework方法取名的良苦用心了。上述Process.start()只是作为调用链中转站,真正发起应用进程的启动请求正是通过此处的startViaZygote()方法完成。

// frameworks/base/core/java/android/os/Process.java
    private static 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[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
            //part1 启动参数的设置
            ArrayList<String> argsForZygote = new ArrayList<String>();
            argsForZygote.add("--runtime-init");//关注点
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
            
            //...
            
			//要启动的进程class,一般为ActivityThread
            argsForZygote.add(processClass);//关注点

            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }
            //part2 连接socket,发送进程启动请求并获取结果
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);//此处之后展开来说
        }
    }

  argsForZygote集合用于存放进程启动配置项,这些配置项一般以–开头且是有序存放,比较重要的是我们的启动类processClass即ActivityThread也存放到了这个集合中。此处配置项的处理乃是startViaZygote()方法的第一部分内容。

  在处理完应用进程的启动配置项后,接着就要着手创建进程了,这就是通过方法openZygoteSocketIfNeeded()和zygoteSendArgsAndGetResult()完成的,前者负责创建socket连接Zygote进程,后者则通过socket完成进程配置项的传递并获取启动结果。

连接Zygote

  上一小节的末尾我们知道了Process.start()方法启动进程最终是通过连接Zygote的server socket来实现的。那么Zygote又是什么,它从哪里来,又负责什么内容?以及通过socket方式有是如何connect完成进程间通信呢?

  Zygote是一个孵化器进程,它由init进程中通过解析init.rc方式启动,属于安卓系统的主要进程之一,其职责主要负责孵化所有的java进程。java应用程序启动及运行一般都需要一个承载进程,这个需求通过android socket的方式(注意此处并非java socket,java socket通过监听端口形式完成信息传递,而android socket则是通过文件读写方式传递消息的,机制完全不同)传给Zygote进程,Zygote则内部通过fork函数变异出一个子进程来作为这个java应用程序的承载进程。

  那么为什么java程序的进程创建一定要通过Zygote进程呢?

  这是由于Zygote进程中,在fork之前预先已将Android的java层字节码文件、系统资源等load到了内存中,这样fork出来的新进程就不需要反复做这些工作了。我们所熟知的system_server进程就是通过Zygote孵化出来的一个典型例子。

  我们续着上一小节的内容,来看看Process是如何创建socket以及连接Zygote进程的。

// frameworks/base/core/java/android/os/Process.java
    public static final String ZYGOTE_SOCKET = "zygote";//socket 1 
    public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";//socket 2
    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);
            }
        }//连接socket,如若连接成功则返回

        if (primaryZygoteState.matches(abi)) {
            return primaryZygoteState;
        }
        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
            try {
            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
            }
        }//socket1没有连上,则继续尝试socket2,同样连上则返回

        if (secondaryZygoteState.matches(abi)) {
            return secondaryZygoteState;
        }
        //若两个socket都没有连上,则返回异常
        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
    }

  Zygote进程一共为进程孵化准备了两个sokcet通道,这在openZygoteSocketIfNeeded()方法中也可以看出来。上述方法先试图连接primaryZygoteState主socket(socket名称为zygote),如果主socket连接不上,则再试图连接secondaryZygoteState即副socket(socket名称为zygote_secondary),如若两者都连接失败则说明打开socket通道失败,返回异常。

  在openZygoteSocketIfNeeded()中,android socket的连接是由ZygoteState的connect()方法完成。

// frameworks/base/core/java/android/os/Process.java$ZygoteState
        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);//从Zygote获取支持的abi列表

            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                    Arrays.asList(abiListString.split(",")));
        }

  ZygoteState是Process的内部类,其connect方法所传入的参数就是连接socket所需要的socket名称,在这个方法中,试图打开一个指定名称的socket通道,在查询完系统所支持的abi架构后把socket通道的I/O流封装成ZygoteState对象(有兴趣的话,读者自行可研究下android socket的实现与使用方式,此处不展开说明)。

  此处connect()方法负责连接名为zygote的server socket,下边我们来大概了解下socket通信目标端是如何创建的。

  Zygote进程由init进程启动后,在main函数执行过程中会调用registerZygoteSocket()方法注册一个server socket,如下:

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
    private static void registerZygoteSocket(String socketName) {
        if (sServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
            try {
                String env = System.getenv(fullSocketName);//在init的service_start中被保存
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
            }

            try {
                sServerSocket = new LocalServerSocket(
                        createFileDescriptor(fileDesc));//创建server socket
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }

  LocalServerSocket作为基于C/S的socket机制的Server端,其创建有两种方式:(一)、指定socket名称创建,(二)、根据指定的FD(文件描述符)创建。而此处Zygote进程则使用了文件描述符的方式来创建,FD以 K(name)-V(fd) 的形式作为环境变量预先存放在系统中,registerZygoteSocket()方法中通过System.getenv()将之前保存的fd取出来。

  那么有个很显然的问题,对应name的fd是什么时候存放到系统环境中去的呢?

  在init进程中,各系统服务启动是通过service_start()完成的,在service_start()中如果发现要启动的服务包含了socket启动需求,则会并通过publish_socket()将创建好的fd保存到environment中。

// system/core/init/init.c
static void publish_socket(const char *name, int fd)
{
    char key[64] = ANDROID_SOCKET_ENV_PREFIX;//system\core\include\cutils\sockets.h中定义 :ANDROID_SOCKET_
    char val[64];

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
            name,
            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
    snprintf(val, sizeof(val), "%d", fd);//将fd拷贝到val中
    add_environment(key, val);//加入环境变量中

    fcntl(fd, F_SETFD, 0);
}

  可以看到的是,所有的fd保存所对应的key都增加了前缀,这个前缀具体的值如下:

// system/core/include/cutils/sockets.h
#define ANDROID_SOCKET_ENV_PREFIX	"ANDROID_SOCKET_"   //所有fd的key前缀
#define ANDROID_SOCKET_DIR		"/dev/socket"   //socket以驱动形式存在

  例如此处我们的Zygote启动时,那么系统环境所保存的fd所对应的的key的全名称为ANDROID_SOCKET_zygote。此处需要注意的是key并不意味着就是/dev/socket驱动下对应socket通道名称,它只是environment中的键而已,socket通道真正的名称还是需要看create_socket()时所传入的si->name(对应zygote进程,则name即为zygote)。

  经此,我们大概了解了Zygote进程注册socket的过程。在其LocalServerSocket创建完成后,Zygote进程就会进入循环监听对这个server socket的请求(即runSelectLoop()方法,其内部以循环方式调用socket的accept()监听请求,下一小节会有介绍)。

  再回到我们client端,在openZygoteSocketIfNeeded()尝试连接zygote并返回ZygoteState后,接下来我们就可以通过ZygoteState进行进程启动参数的传输与结果的获取了,这就是zygoteSendArgsAndGetResult()的处理任务。

// frameworks/base/core/java/android/os/Process.java
    private static ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        try {
            final BufferedWriter writer = zygoteState.writer;
            final DataInputStream inputStream = zygoteState.inputStream;
            //part1 写入部分
            writer.write(Integer.toString(args.size()));//首先保存参数数量,方便zygote按数量解析参数
            writer.newLine();

            int sz = args.size();
            for (int i = 0; i < sz; i++) {//进程启动参数的保存
                String arg = args.get(i);
                if (arg.indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx(
                            "embedded newlines not allowed");
                }
                writer.write(arg);
                writer.newLine();
            }
            writer.flush();//将buffer交给zygote处理

            //part2 读取部分
            ProcessStartResult result = new ProcessStartResult();
            result.pid = inputStream.readInt();//获取返回结果,pid - 进程号
            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            result.usingWrapper = inputStream.readBoolean();
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
    }

  zygoteSendArgsAndGetResult()分为向zygote写入数据和读取zygote结果两个部分,需要注意的是整个过程(主要是读取过程)是阻塞式的。

  就方法的写入部分来看,首先写入了启动参数的数量,接下来才将进程启动配置的集合数据分条写入buffer,最后通过flush操作把整个写入部分数据通过socket通道的OutputStream传输给zygote(socket通道一般由I/O两部分组成)。

  就方法的读取部分来看,主要是读取了fork()函数返回的进程id并封装为ProcessStartResult返回给AMS。

进程的创建过程

  上述我们讲到zygoteSendArgsAndGetResult()中向zygote进程写入了socket数据,那么socket到底是如何接收这些数据,又是如何处理的呢?带着这些疑问,我们接着往下分析。

  我们先来看看Zygote进程main()函数socket相关的主体代码:

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    public static void main(String argv[]) {
        try {
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();

            boolean startSystemServer = false;
            String socketName = "zygote";
            //...
            registerZygoteSocket(socketName);
            //...
            runSelectLoop(abiList);//内部包含fork相关调用

            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();//fork后的子进程进入此处
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

  在注册server socket后,Zygote就调用runSelectLoop()进入循环以接收来自client socket的请求,这是一种阻塞式循环的方式。

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        FileDescriptor[] fdArray = new FileDescriptor[4];
        fds.add(sServerSocket.getFileDescriptor());//fds与peers成对出现
        peers.add(null);//此处fds中已放入server socket的fd

        int loopCount = GC_LOOP_COUNT;
        while (true) {
            int index;
            //...
            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) {//0表示server socket,说明此时客户端peers已经处理完成。
                ZygoteConnection newPeer = acceptCommandPeer(abiList);//内部调用accept()阻塞式等待client连接
                peers.add(newPeer);
                fds.add(newPeer.getFileDescriptor());
            } else {//index大于0,说明有peer要处理
                boolean done;
                done = peers.get(index).runOnce();//处理client请求

                if (done) {//处理完成后删除对应信息
                    peers.remove(index);
                    fds.remove(index);
                }
            }
        }
    }

  在runSelectLoop()方法中,有两组集合我们需要关注一下,它们中的元素对应了来自client端的一次次socket连接请求。

  • fds:用于装载每次连接的文件描述符。
  • peers:内部元素类型是ZygoteConnection,代表了来自client发起的一次socket连接。

  这两组集合在还没有进入while循环之前队首元素已经server socket被填充,至于为什么有此次填充,这个我也比较困惑,有知情的读者可留言告知下。

  在上述方法进入循环后,会试图从fds中寻找到可读的描述符,因之前队首已经被填充了元素,可想而知selectReadable()方法正常情况下返回值至少大于等于0(此处返回的是下角标,0即队首元素)。如果index==0,则说明可读元素即server socket自身,此刻并无client端socket请求到来,所以会调用acceptCommandPeer()以阻塞式等待socket请求的到来。反之,若index角标大于0,则说明此时fds中已经有至少2个可读文件描述符(server socket +至少1个client socket请求),此时就需要处理对应的peer连接请求。

  client端socket请求的处理是通过调用ZygoteConnection的runOnce()方法完成的。

// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;
        //part1 读取进程启动参数
        try {
            args = readArgumentList();//将client端的进程启动参数取出
            descriptors = mSocket.getAncillaryFileDescriptors();//Ancillary-附加的,辅助的
        } catch (IOException ex) {
            closeSocket();
            return true;
        }
        //...
        
        try {
            //part2 解析进程启动参数,如果需要则处理支持的abi架构查询
            parsedArgs = new Arguments(args);//解析进程启动参数
            if (parsedArgs.abiListQuery) {//查询abi
                return handleAbiListQuery();//返回abi长度及内容
            }
            //... 省略掉权限检查部分
            //part3 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 (IOException ex) {
            //...此处包含多个catch,省略掉
        }
        //part4 此处一分为二,分别对原进程与新进程进行处理
        try {
            if (pid == 0) {//子进程
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);//进入子进程处理流程,内部最终通过throw异常方式调用ActivityThread.main()
                return true;
            } else {//zygote进程
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);//将fork出来的子进程的id等返回client端
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }

  runOnce方法可以分为4个部分来解读:

  1. 读取client端传过来的进程启动配置项。根据之前Process中zygoteSendArgsAndGetResult()方法对进程配置项的保存过程来看,可以猜想此处readArgumentList()首先读取的是配置项的个数,然后根据个数一行一行把配置项读取并村放入args字符串数组中。

  2. 解析进程启动参数,如果需要则处理abi查询。解析过程是通过Arguments类完成的,其构造方法调用parseArgs()解析所有传输过来的进程启动参数,并将这些参数保存在Arguments对象本身的各成员变量中,包括一些关键参数如uid、gid、runtime-init、进程启动类(被放入remainingArgs中)等。abi架构的查询则是根据abiListQuery参数是否为true来确定,如果是abi查询,则进入handleAbiListQuery()流程把当前系统支持的cpu架构返回给client端。值得注意的是一般情况下Process.start()启动进程时其内部会主动查询系统所支持的abi架构的。

  3. fork进程。fork过程是通过调用Zygote.forkAndSpecialize()方法完成的,其最终会通过JNI调用到native层,最终是调用linux的fork函数完成进程的创建。在这个方法被调用之后,接下来runOnce()及之后zygote的处理流程就一分为二,分为新建的子进程和原zygote进程两部分了。关于fork()函数的特点,读者可自行查看fork( )函数详解这篇文章。

  4. 分别对原进程与新进程进行处理。根据fork()函数一次调用两次返回的特点,如果fork返回的pid是0,则说明当前正处于新建进程中,如果pid大于0(pid的值是新建进程的pid值),则当前处于zygote进程中。由此,这第四部分又细化为两个子部分。

    (一):当前处于子进程中。调用handleChildProc()方法执行进程初始化操作。此子部分在下边详细说明。

    (二):当前处于zygote进程中。接着调用handleParentProc()方法将创建的进程id等通过socket返回给client端(即Process.start()的调用方,本文指的则是AMS所在的system_server进程)。

  下边我们来详细说明当前处于子进程中时的情况,即handleChildProc()方法及后续调用过程。

// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
    private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        //...
        
        if (parsedArgs.runtimeInit) {
            if (parsedArgs.invokeWith != null) {
                WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        pipeFd, parsedArgs.remainingArgs);
            } else {//根据Process.start所配置的参数来看,进入的是此处
                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                        parsedArgs.remainingArgs, null /* classLoader */);
            }
        } else {
           //...
        }
    }

  在上述代码中,会根据进程启动参数配置的不同而进入不同的处理逻辑,此处我们仅就本文研究的AMS发起创建进行解读。在Process关于进程启动参数的配置方法startViaZygote()中,第一个被添加的配置项是:–runtime-init。对应到此处parseArgs解析的就是runtimeInit成员为true,而又因没有配置–invoke-with参数,所以在handleChildProc()方法中,进入的处理逻辑是RuntimeInit的zygoteInit()方法。

  需要注意的是zygoteInit()所传入的进程启动参数只是部分了,即还未使用的remainingArgs保留参数(这其中就有最重要的进程启动类)。此方法具体处理过程如下:

// frameworks/base/core/java/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");

        redirectLogStreams();
        commonInit();//进程初始化
        
		//此处最终在app_main.cpp中执行onZygoteInit方法启动一个binder线程
		//这也是java进程天生支持binder机制的原因
        nativeZygoteInit();
		
        applicationInit(targetSdkVersion, argv, classLoader);//内部最终进入ActivityThread.main
    }

  在zygoteInit()方法中我们可以看到对应用进程的一些初始化的过程,如nativeZygoteInit()就将为当前进程启动一个binder主线程,使得当前进程支持binder跨进程调用,这也是为什么后续的ActivityThread.main()方法能够与AMS进行通信的原因。

  同时我们继续往下看,进入applicationInit()方法中:

// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
    private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        nativeSetExitWithoutCleanup(true);
        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);

        final Arguments args;
        try {
            //需要注意的是,此Arguments并非同ZygoteConnection中的Arguments类,这是两个解析方式不同的类
            args = new Arguments(argv);
        } catch (IllegalArgumentException ex) {
            Slog.e(TAG, ex.getMessage());
            return;
        }
        //调用ActivityThread的main()函数,其内部的调用方式比较特别哟
        invokeStaticMain(args.startClass, args.startArgs, classLoader);
    }

  在applicationInit()中首先对VM进行了一些配置,然后对保留下来的启动参数继续进行解析,解析得来的startClass就是进程启动类ActivityThread,而剩余的args则最终会以参数形式传到ActivityThread.main(args)中去。

  经历了那么多的风风雨雨,应用进程启动的关键点来了,这就是applicationInit()中最后调用的invokeStaticMain()方法,从名称可以看出,其中必然涉及到反射调用,且调用的是静态的类方法main()。

  invokeStaticMain()方法具体内容如下:

// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
    private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        Class<?> cl;
        try {
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className,
                    ex);
        }
        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }

        int modifiers = m.getModifiers();
        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
            throw new RuntimeException(
                    "Main method is not public and static on " + className);
        }
        //关键点,通过throw方式执行方法的invoke,有利于将方法栈清除。
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }

  上述方法中,className即AMS的startProcessLocked()方法所传入并流转到此的ActivityThread类,当我们通过java反射方式找到ActivityThread.main()方法后,就将Method与启动参数以MethodAndArgsCaller异常的形式抛出,由子进程进行捕获。

  为什么我们说这样的throw方式来达到调用目标比较特别,因为通过throw方式调用方法可以有效去除之前的方法调用链。真因此,我们在查询应用进程的方法栈时,发现其方法调用链最多能追溯到ZygoteInit.main()方法(因为在其main()方法中对此异常进行了捕获)。

  invokeStaticMain()方法最后所抛出的MethodAndArgsCaller异常,最终由ZygoteInit.main()捕获。值得注意的是,因为runSelectLoop()执行了fork函数一分为二,而且MethodAndArgsCaller异常也是有新建的子进程抛出,所以下边的代码也是在子进程中执行的。

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    public static void main(String argv[]) {
        try {
            //...
            runSelectLoop(abiList);
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();//捕获异常,值得注意的是,只有子进程才会抛出这个异常
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

  可见,异常被捕获后执行了其run()方法。这个run()方法就是真正invoke()调用ActivityThread.main()的地方了。

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java$MethodAndArgsCaller
        public void run() {
            try {
                mMethod.invoke(null, new Object[] { mArgs });
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InvocationTargetException ex) {
                Throwable cause = ex.getCause();
                if (cause instanceof RuntimeException) {
                    throw (RuntimeException) cause;
                } else if (cause instanceof Error) {
                    throw (Error) cause;
                }
                throw new RuntimeException(ex);
            }
        }

  至此,连接Zygote并创建承载进程部分已经完成了。但对于本文所探讨的AMS应用进程创建流程却还得继续。

返回创建结果

  从上一小节我们可知,应用进程被fork出来之后最终要执行ActivityThread.main()方法。这其实是一个很经典的流程,Android系统中当启动一个新进程时都会执行fork函数,并由fork返回值来执行新进程所指定的main()方法。例如system_server进程在启动时会指定执行SystemServer.main()函数,而对于普通java应用而言,则是此处的ActivityThread.main()。

  ActivityThread.main()方法如下:

// frameworks/base/core/java/android/app/ActivityThread.java
    public static void main(String[] args) {
    
        //...
        
        Looper.prepareMainLooper();//MainLooper的创建,这也是为什么之后我们一颗直接获取的原因
        
        ActivityThread thread = new ActivityThread();
        thread.attach(false);//其中包含了向AMS报告启动信息
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        AsyncTask.init();//异步任务池的创建
        
        Looper.loop();//进入消息循环

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

  从main()方法的调用逻辑来看,我们可以发现Handler消息机制的使用(对handler机制不了解的读者可以阅读深入理解Handler机制这篇文章),这证明了android应用程序是基于消息驱动的观点。而且为了方便调用ActivityThread的非静态方法,main()中新建了ActivityThread对象,并调用其attach()方法与AMS进行绑定。

  attach()方法在之前深入理解AMS之启动过程一文中我们也介绍过,不过当时是站在系统进程的角度来解读。而对于此处而言,attach()方法执行的是普通应用进程的逻辑。

// frameworks/base/core/java/android/app/ActivityThread.java
    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
            //...
        } else {
            //...省略系统进程的创建过程
        }
        //...
    }

  在attach()方法中与AMS进行绑定是通过AMS.attachApplication()完成的,需要注意的是attachApplication()方法传入的mAppThread对象,mAppThread是一个binder对象,它继承自IApplicationThread,主要负责AMS与应用进程的通信工作,在之前ProcessRecord中的thread成员就是此处传过去的。

  此处进行的attach工作呗AMS接收到之后,AMS就可以将当前进程的激活、移除启动超时消息、并检查进程是否有待做工作如Activity的启动等等。

  至此,AMS应用进程的启动就已告一段落。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值