Android中间层分析1.【AMS】进程的启动-Process.start分析

进程创建前

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);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }

start函数里面没有做太多的事情,直接交给了startViaZygote

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) {
            ArrayList<String> argsForZygote = new ArrayList<String>();

            // --runtime-args, --setuid=, --setgid=,
            // and --setgroups= must go first
            argsForZygote.add("--runtime-args");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
                argsForZygote.add("--enable-jni-logging");
            }
            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
                argsForZygote.add("--enable-safemode");
            }
  ....

            argsForZygote.add(processClass);

            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }

            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
        }
    }

startViaZygote 函数前面都是对参数的整理。后面交给了zygoteSendArgsAndGetResult,
但是这里我们需要注意的这边 openZygoteSocketIfNeeded 会打开一个socket,用于和zygote通讯,这个zygote之所以要一个abi参数因为在64位系统中有两个zygote进程

root      264   1     1173156 127704          0 0000000000 S zygote64
root      265   1     934112 114496          0 0000000000 S zygote

就分别通讯的意思。

private static ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        try {
            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                if (args.get(i).indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx("embedded newlines not allowed");
                }
            }

            final BufferedWriter writer = zygoteState.writer;
            final DataInputStream inputStream = zygoteState.inputStream;

            writer.write(Integer.toString(args.size()));
            writer.newLine();

            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                writer.write(arg);
                writer.newLine();
            }

            writer.flush();

            ProcessStartResult result = new ProcessStartResult();

            result.pid = inputStream.readInt();
            result.usingWrapper = inputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
    }

上一步拿到了zygoteState 现在进行通讯,首先进行的参数的校验,如果没有问题就通过一个一个参数write传输过去给zygote。zygote拿到这些参数就会给你创建好需要的进程。然后返回结果通过read读取出来。
那么zygote那边是怎么创建进程呢?我们来看下zygote那边的工作。

进程的创建

zygote循环

zygoteInit.main()函数是zygote启动的时候会执行的函数,关于zygote启动这里不在详细解析。

public static void main(String argv[]) {
        try {
            runSelectLoop(abiList);
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            closeServerSocket();
            throw ex;
        }
    }

在main函数中会调用。runSelectLoop开启socket等待。我们这里留意下这个MethodAndArgsCaller异常。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        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);
                    }
                }
            }
        }
    }

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;
        }

       
        try {
            parsedArgs = new Arguments(args);
            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) {

                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

                // should never get here, the child is expected to either
                // throw ZygoteInit.MethodAndArgsCaller or exec().
                return true;
            } else {
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }

这里关键的地方就是forkAndSpecialize 前面都是收集参数等待。后forkAndSpecialize fork进程后,父进程和紫禁城分道扬镳。----这个是linux进程创建的知识了。
接下来我们要分开两条不同关注点去看进程的创建了

  • forkAndSpecialize 会调用linux的fork系统调用创建进程,创建后我们关注它的一些环境的建立。
  • handleChildProc 进程创建后回去加载app的入口也就是ActivityThread。我们关注它是怎去加载的
    如果是单单看应用的启动,往应用层去理解呢,其实不太需要知道fork流程,如果想更深入了解系统的运行机制,可以一起来看下forkAndSpecialize到底做来什么东西。

进程的fork

上面讲到forkAndSpecialize 函数,我们这节的目的,看下forkAndSpecialize是怎么到底层调用linux的fork系统调用,从而开辟一个进程的。要了解fork系统调用和运用的需要去了解linux的应用开发。这样才比较好了解进程的启动,在linux里面为什么用一个fork就创建了一个进程。这个是需要一个基础知识的。

public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
          String instructionSet, String appDataDir) {
...
        int pid = nativeForkAndSpecialize(
                  uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                  instructionSet, appDataDir);
...
        return pid;
    }
    
    native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
      int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
      String instructionSet, String appDataDir);

我们来看到forkAndSpecialize 什么都没有做直接交给了nativeForkAndSpecialize,而nativeForkAndSpecialize是一个jni底层的函数。
这个函数的实现在com_android_internal_os_Zygote.cpp (frameworks\base\core\jni)

static const JNINativeMethod gMethods[] = {
    { "nativeForkAndSpecialize",
      "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
      (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },

在这个com_android_internal_os_Zygote.cpp 文件里面,我们看到它的jni实现是com_android_internal_os_Zygote_nativeForkAndSpecialize

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
        JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
        jint debug_flags, jobjectArray rlimits,
        jint mount_external, jstring se_info, jstring se_name,
        jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {
    jlong capabilities = 0;
    return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
            rlimits, capabilities, capabilities, mount_external, se_info,
            se_name, false, fdsToClose, instructionSet, appDataDir);
}

com_android_internal_os_Zygote_nativeForkAndSpecialize 这个函数也没有做太多的事情,直接交给了ForkAndSpecializeCommon.

static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                     jint debug_flags, jobjectArray javaRlimits,
                                     jlong permittedCapabilities, jlong effectiveCapabilities,
                                     jint mount_external,
                                     jstring java_se_info, jstring java_se_name,
                                     bool is_system_server, jintArray fdsToClose,
                                     jstring instructionSet, jstring dataDir) {
  SetSigChldHandler();
...
  pid_t pid = fork();

  if (pid == 0) {
    ..

    if (!is_system_server) {
        int rc = createProcessGroup(uid, getpid());
        if (rc != 0) {
            if (rc == -EROFS) {
                ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
            } else {
                ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
            }
        }
    }

    SetGids(env, javaGids);

    ...

    int rc = setresgid(gid, gid, gid);
   ...
    SetCapabilities(env, permittedCapabilities, effectiveCapabilities);

    SetSchedulerPolicy(env);

    ...
    rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
    ...
    if (se_info_c_str != NULL) {
      SetThreadName(se_name_c_str);
    }

   ...
  } else if (pid > 0) {
   ...
  }
  return pid;
}

这个函数做的事情就有点多了,关键的是我们看到了fork()函数。

  • 在这个fork()函数之前做的是一些signal的设置
  • fork()完成了以后兵分两路,子进程会去做很多gid 、scheduler 和 selinux等等的设置。
    到这里我们就完整的看到了一个进程创建的过程。返回pid。

handleChildProc

现在进程出来了,我们需要一路返回,看看我们的进程会去做那些工作,是怎么走入到我们的apk代码里面的,主要是走到ActivityThread的过程。
我们回到handleChildProc,里面来。

private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(),
                    pipeFd, parsedArgs.remainingArgs);
        } else {
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, null /* classLoader */);
        }
        }
    }

我们的进程已经创建完成来。handleChildProc是首先会调用的函数,而这个函数又调用来RuntimeInit.zygoteInit,为什么是讲这个函数而不去讲上面的函数,我这里讲一个简单的linux知识。
linux进程创建也是一样的会直接fork,fork完成后如果你要加载代码一般是用execv系统调用去加载代码的,但是Android,使用的是java虚拟机,所以,上面的流程是给一些本地进程走的。而java是通过Class进行类加载,来我们来一口气读完下面三个函数。看下是怎么类加载的。

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
...
        applicationInit(targetSdkVersion, argv, classLoader);
    }
    private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
       ...
        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);

       ...
        invokeStaticMain(args.startClass, args.startArgs, classLoader);
    }
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) {
...
        }
                Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
     ...
        }
...
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }

这里我们关注下面流程:

  • 1.首先设置虚拟机的环境
  • 2.调用invokeStaticMain
  • 3.在invokeStaticMain,我们找到这个类也就是ActivityThread类,这到这个类的Method,也就是main函数。
  • 4.最后居然没有运行这个类而是抛出一个异常。很匪夷所思,根据反射调用的话,应该是要 m.invoke(null, arg);才对的。
    那么它抛出这个异常是在哪里catch的呢?还记得文章最前面我们说要关注的抛出的异常吗?在前面zygote循环的时候我说要关注的异常
public static void main(String argv[]) {
        try {
            runSelectLoop(abiList);
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            closeServerSocket();
            throw ex;
        }
    }

他就是在这里catch了这个异常然后调用了这个异常的run函数。

public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run() {
            try {
                mMethod.invoke(null, new Object[] { mArgs });
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InvocationTargetException ex) {
...
                throw new RuntimeException(ex);
            }
        }

然后再这里进行了invoke反射调用。

至于为什么要通过抛出异常的方法去做这个调用而不是直接调用,网上早有人给出了答案。这里就不多说。
到现在整个进程启动的流程就结束了。ActivityThread类加载起来以后,会和ams交互,接下来会调用到你的activity的onCreate方法,onResume方法。然后你的apk就完美运行了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: ams-server-2022-07-21.rar 是一个文件的名称。这个文件是一个压缩包,文件格式为.rar。根据文件名可以推测,这个文件是关于"ams-server"的,日期为2022年7月21日。 "ams-server"代表了一个可能是"Attendance Management System Server"(考勤管理系统服务器)的缩写。这个服务器可能是一个用于管理和处理考勤系统数据的应用程序。 通过这个压缩包,我们可以猜测其中包含了与该考勤管理系统服务器相关的文件和数据。可能会有服务器的配置文件、源代码、数据库备份文件或其他相关的文档和资料。 如果需要使用这个压缩包,首先需要将其解压缩。通常情况下,我们可以使用压缩软件(比如WinRAR、7-Zip等)来解压缩RAR文件。在解压缩之后,我们可以查看其中的内容,并按照需求进行相应的操作。 总而言之, "ams-server-2022-07-21.rar"是一个压缩文件,可能包含了与考勤管理系统服务器相关的文件和数据。不同的应用场景可能需要不同的操作和处理。 ### 回答2: ams-server-2022-07-21.rar是一个文件的名称。根据名称可以猜测它是一个压缩文件,并且可能与ams服务器的某个版本或日期相关。根据“-2022-07-21”的部分,可以猜测这个压缩文件可能是为2022年7月21日的ams服务器版本制作的。 根据常见的命名规则,"ams-server"可能是指ams服务器的名称或缩写。服务器是一种计算机程序,用于提供服务、管理资源和处理请求。ams可能是一个特定的项目、软件或系统。压缩文件通常用于将多个文件或文件夹压缩成一个单独的文件,以便在网络上传输或存储时占用更少的空间。 因此,ams-server-2022-07-21.rar可能是一个存档了该日期的ams服务器相关文件的压缩文件。使用解压缩软件可以将压缩文件解压缩,以获得存档内的所有文件和文件夹。解压后的文件可能包含ams服务器的程序、配置文件、日志文件或其他与服务器操作相关的文件。 需要注意的是,这只是对文件名称的推测,实际内容可能有所不同。要确切了解ams-server-2022-07-21.rar文件的内容和用途,需要进一步查看文件、文档或与文件相关的信息。 ### 回答3: ams-server-2022-07-21.rar 是一个压缩文件,扩展名为.rar。根据名称可以推测,这个文件可能是一个AMS服务器的软件包,版本为2022年7月21日。rar 是一种流行的压缩格式,通常用于将多个文件或文件夹打包成一个单独的文件。通过解压缩该文件,我们可以获得其中的内容。 解压缩过程首先需要一个解压软件,如WinRAR或7-Zip等。我们可以将ams-server-2022-07-21.rar 文件拖放到解压软件的窗口中,或者使用软件的解压缩功能。解压缩后会生成一个或多个文件和文件夹,这些文件和文件夹可能包含了AMS服务器的程序文件、配置文件、文档等。 根据文件名中的日期,可以判断这个软件包是在2022年7月21日创建的。这可能表示该版本的AMS服务器在该日期之前是最新的,提供了一些新功能、修复或改进。用户可以下载和安装这个软件包,以使用其中提供的更新功能,或者修复现有版本的问题。 总之,ams-server-2022-07-21.rar 是一个AMS服务器的压缩软件包,文件名反映了其创建日期。通过解压缩该文件,我们可以访问其中的内容,并使用其中的文件来更新或改进现有的AMS服务器。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈万三djh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值